@bleedingdev/modern-js-create 3.4.0-ultramodern.0 → 3.4.0-ultramodern.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +7 -7
  2. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +24 -0
  3. package/dist/cjs/ultramodern-workspace/contracts.cjs +8 -4
  4. package/dist/cjs/ultramodern-workspace/demo-components.cjs +14 -21
  5. package/dist/cjs/ultramodern-workspace/descriptors.cjs +2 -2
  6. package/dist/cjs/ultramodern-workspace/effect-api.cjs +162 -31
  7. package/dist/cjs/ultramodern-workspace/module-federation.cjs +29 -10
  8. package/dist/cjs/ultramodern-workspace/package-json.cjs +102 -14
  9. package/dist/cjs/ultramodern-workspace/versions.cjs +1 -5
  10. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +7 -0
  11. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +6 -8
  12. package/dist/esm/ultramodern-workspace/add-vertical.js +25 -1
  13. package/dist/esm/ultramodern-workspace/contracts.js +9 -5
  14. package/dist/esm/ultramodern-workspace/demo-components.js +14 -21
  15. package/dist/esm/ultramodern-workspace/descriptors.js +2 -2
  16. package/dist/esm/ultramodern-workspace/effect-api.js +162 -31
  17. package/dist/esm/ultramodern-workspace/module-federation.js +27 -11
  18. package/dist/esm/ultramodern-workspace/package-json.js +87 -11
  19. package/dist/esm/ultramodern-workspace/versions.js +2 -3
  20. package/dist/esm/ultramodern-workspace/workspace-scripts.js +5 -1
  21. package/dist/esm/ultramodern-workspace/write-workspace.js +9 -11
  22. package/dist/esm-node/ultramodern-workspace/add-vertical.js +25 -1
  23. package/dist/esm-node/ultramodern-workspace/contracts.js +9 -5
  24. package/dist/esm-node/ultramodern-workspace/demo-components.js +14 -21
  25. package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -2
  26. package/dist/esm-node/ultramodern-workspace/effect-api.js +162 -31
  27. package/dist/esm-node/ultramodern-workspace/module-federation.js +27 -11
  28. package/dist/esm-node/ultramodern-workspace/package-json.js +87 -11
  29. package/dist/esm-node/ultramodern-workspace/versions.js +2 -3
  30. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +5 -1
  31. package/dist/esm-node/ultramodern-workspace/write-workspace.js +9 -11
  32. package/dist/types/ultramodern-workspace/module-federation.d.ts +1 -0
  33. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -2
  34. package/dist/types/ultramodern-workspace/versions.d.ts +1 -2
  35. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +1 -0
  36. package/package.json +4 -4
  37. package/template-workspace/AGENTS.md.handlebars +2 -1
  38. package/template-workspace/README.md.handlebars +7 -0
  39. package/template-workspace/oxfmt.config.ts +3 -1
  40. package/template-workspace/oxlint.config.ts +3 -1
  41. package/template-workspace/patches/@tanstack__router-core@1.171.13.patch +51 -0
  42. package/template-workspace/pnpm-workspace.yaml.handlebars +11 -0
  43. package/template-workspace/scripts/bootstrap-agent-skills.mjs +42 -10
  44. package/templates/app/shell-frame.tsx +1 -2
  45. package/templates/app/ultramodern-route-head.tsx.handlebars +22 -11
  46. package/templates/packages/shared-contracts-index.ts +206 -272
  47. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +9 -0
  48. package/templates/workspace-scripts/ultramodern-typecheck.mjs +197 -0
  49. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +234 -2
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import {
5
+ accessSync,
6
+ chmodSync,
7
+ constants,
8
+ existsSync,
9
+ mkdirSync,
10
+ } from 'node:fs';
11
+ import os from 'node:os';
12
+ import { dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const args = process.argv.slice(2);
16
+ const workspaceRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
17
+ const cpuCount = Math.max(
18
+ 1,
19
+ typeof os.availableParallelism === 'function'
20
+ ? os.availableParallelism()
21
+ : os.cpus().length,
22
+ );
23
+
24
+ function fail(message) {
25
+ console.error(message);
26
+ process.exit(1);
27
+ }
28
+
29
+ function parsePositiveInt(value, label) {
30
+ const parsed = Number(value);
31
+ if (!Number.isInteger(parsed) || parsed < 1) {
32
+ fail(`${label} must be a positive integer.`);
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ function envPositiveInt(name, fallback) {
38
+ const value = process.env[name]?.trim();
39
+ return value ? parsePositiveInt(value, name) : fallback;
40
+ }
41
+
42
+ function readArgs() {
43
+ let buildTarget;
44
+ let projectTarget;
45
+ let checkers;
46
+ let builders;
47
+ const passthrough = [];
48
+
49
+ for (let index = 0; index < args.length; index += 1) {
50
+ const arg = args[index];
51
+ const next = args[index + 1];
52
+
53
+ if (arg === '--') {
54
+ passthrough.push(...args.slice(index + 1));
55
+ break;
56
+ }
57
+
58
+ if (arg === '--build' || arg === '-b') {
59
+ buildTarget = next && !next.startsWith('-') ? next : 'tsconfig.json';
60
+ if (buildTarget === next) {
61
+ index += 1;
62
+ }
63
+ continue;
64
+ }
65
+
66
+ if (arg === '--project' || arg === '-p') {
67
+ if (!next || next.startsWith('-')) {
68
+ fail(`${arg} requires a tsconfig path.`);
69
+ }
70
+ projectTarget = next;
71
+ index += 1;
72
+ continue;
73
+ }
74
+
75
+ if (arg === '--checkers') {
76
+ if (!next || next.startsWith('-')) {
77
+ fail('--checkers requires a positive integer.');
78
+ }
79
+ checkers = parsePositiveInt(next, '--checkers');
80
+ index += 1;
81
+ continue;
82
+ }
83
+
84
+ if (arg === '--builders') {
85
+ if (!next || next.startsWith('-')) {
86
+ fail('--builders requires a positive integer.');
87
+ }
88
+ builders = parsePositiveInt(next, '--builders');
89
+ index += 1;
90
+ continue;
91
+ }
92
+
93
+ passthrough.push(arg);
94
+ }
95
+
96
+ if (buildTarget && projectTarget) {
97
+ fail('Choose either --build or --project, not both.');
98
+ }
99
+
100
+ return {
101
+ mode: buildTarget ? 'build' : 'project',
102
+ target: buildTarget ?? projectTarget ?? 'tsconfig.json',
103
+ checkers,
104
+ builders,
105
+ passthrough,
106
+ };
107
+ }
108
+
109
+ function resolveTsgoBinary() {
110
+ const explicitBinary = process.env.EFFECT_TSGO_BIN || process.env.TSGO_BIN;
111
+ if (explicitBinary) {
112
+ return explicitBinary;
113
+ }
114
+
115
+ const cli = process.env.EFFECT_TSGO_CLI || 'effect-tsgo';
116
+ const result = spawnSync(cli, ['get-exe-path'], {
117
+ encoding: 'utf8',
118
+ stdio: ['ignore', 'pipe', 'pipe'],
119
+ });
120
+
121
+ if (result.error) {
122
+ fail(
123
+ `Unable to run ${cli}. Run pnpm install or set EFFECT_TSGO_BIN to the native-preview tsgo binary.`,
124
+ );
125
+ }
126
+ if (result.status !== 0) {
127
+ fail(
128
+ result.stderr?.trim() ||
129
+ `${cli} get-exe-path exited with status ${result.status}.`,
130
+ );
131
+ }
132
+
133
+ return result.stdout.trim() || cli;
134
+ }
135
+
136
+ const parsed = readArgs();
137
+ const defaultBuilders = Math.min(8, Math.max(1, Math.floor(cpuCount / 2)));
138
+ const builders =
139
+ parsed.builders ??
140
+ envPositiveInt('ULTRAMODERN_TSGO_BUILDERS', defaultBuilders);
141
+ const defaultCheckers =
142
+ parsed.mode === 'build'
143
+ ? Math.min(4, Math.max(1, Math.floor(cpuCount / builders)))
144
+ : Math.min(8, Math.max(2, cpuCount - 1));
145
+ const checkers =
146
+ parsed.checkers ??
147
+ envPositiveInt('ULTRAMODERN_TSGO_CHECKERS', defaultCheckers);
148
+ const tsgoBin = resolveTsgoBinary();
149
+
150
+ if (!existsSync(tsgoBin)) {
151
+ fail(`TS-Go binary not found at ${tsgoBin}. Run pnpm install.`);
152
+ }
153
+
154
+ try {
155
+ accessSync(tsgoBin, constants.X_OK);
156
+ } catch {
157
+ chmodSync(tsgoBin, 0o755);
158
+ }
159
+
160
+ mkdirSync(join(workspaceRoot, 'node_modules/.cache/tsgo'), {
161
+ recursive: true,
162
+ });
163
+
164
+ const tsgoArgs =
165
+ parsed.mode === 'build'
166
+ ? [
167
+ '--build',
168
+ parsed.target,
169
+ '--pretty',
170
+ 'false',
171
+ '--checkers',
172
+ String(checkers),
173
+ '--builders',
174
+ String(builders),
175
+ '--stopBuildOnErrors',
176
+ ...parsed.passthrough,
177
+ ]
178
+ : [
179
+ '--project',
180
+ parsed.target,
181
+ '--noEmit',
182
+ '--pretty',
183
+ 'false',
184
+ '--checkers',
185
+ String(checkers),
186
+ ...parsed.passthrough,
187
+ ];
188
+
189
+ const result = spawnSync(tsgoBin, tsgoArgs, {
190
+ stdio: 'inherit',
191
+ });
192
+
193
+ if (result.error) {
194
+ throw result.error;
195
+ }
196
+
197
+ process.exit(result.status ?? 1);
@@ -91,6 +91,8 @@ const expectedRemoteSubsetsForRefs = refs =>
91
91
  .map(expectedRemoteContractSubset);
92
92
  const requiredMicroVerticalPaths = vertical => [
93
93
  `${vertical.path}/package.json`,
94
+ `${vertical.path}/tsconfig.json`,
95
+ `${vertical.path}/tsconfig.mf-types.json`,
94
96
  `${vertical.path}/modern.config.ts`,
95
97
  `${vertical.path}/module-federation.config.ts`,
96
98
  `${vertical.path}/api/effect/index.ts`,
@@ -222,6 +224,15 @@ const assertMicroVerticalContractGraph = ({
222
224
  '.modernjs/ultramodern-generated-contract.json shell moduleFederation.remotes',
223
225
  'regenerate the generated shell Module Federation contract',
224
226
  );
227
+ assertSameJson(
228
+ shellContract.ssr,
229
+ {
230
+ mode: 'string',
231
+ moduleFederationAppSSR: true,
232
+ },
233
+ '.modernjs/ultramodern-generated-contract.json shell SSR contract',
234
+ 'restore generated string SSR Module Federation settings',
235
+ );
225
236
 
226
237
  for (const vertical of fullStackVerticals) {
227
238
  const expectedRefs = vertical.verticalRefs ?? [];
@@ -353,6 +364,7 @@ const assertMicroVerticalContractGraph = ({
353
364
  contract: contractEntry.effect?.contract,
354
365
  client: contractEntry.effect?.client,
355
366
  },
367
+ ssr: contractEntry.ssr,
356
368
  },
357
369
  {
358
370
  kind: 'vertical',
@@ -369,12 +381,136 @@ const assertMicroVerticalContractGraph = ({
369
381
  contract: './shared/effect/api',
370
382
  client: './effect/client',
371
383
  },
384
+ ssr: {
385
+ mode: 'string',
386
+ moduleFederationAppSSR: true,
387
+ },
372
388
  },
373
389
  `.modernjs/ultramodern-generated-contract.json apps.${vertical.id}`,
374
390
  'regenerate the generated MicroVertical contract entry',
375
391
  );
376
392
  }
377
393
  };
394
+ const toPosixPath = value => value.split(path.sep).join('/');
395
+ const referenceFrom = (fromPath, toPath) => ({
396
+ path: toPosixPath(path.relative(fromPath, toPath)),
397
+ });
398
+ const sharedPackagePaths = [
399
+ 'packages/shared-contracts',
400
+ 'packages/shared-design-tokens',
401
+ ];
402
+ const tsgoCacheKey = packagePath => packagePath.replace(/[^a-zA-Z0-9._-]+/gu, '__');
403
+ const assertProjectReferenceEmitConfig = (tsConfig, packagePath) => {
404
+ const compilerOptions = tsConfig.compilerOptions ?? {};
405
+ const relativeRoot = toPosixPath(path.relative(packagePath, '.')) || '.';
406
+ assert(compilerOptions.composite === true, `${packagePath} must stay a composite TS-Go project`);
407
+ assert(compilerOptions.declaration === true, `${packagePath} must emit declarations for TS-Go build mode`);
408
+ assert(compilerOptions.declarationMap === false, `${packagePath} must not emit declaration maps during checks`);
409
+ assert(compilerOptions.emitDeclarationOnly === true, `${packagePath} must only emit declarations during checks`);
410
+ assert(compilerOptions.noEmit === false, `${packagePath} must override root noEmit for TS-Go build mode`);
411
+ assert(
412
+ compilerOptions.outDir === `${relativeRoot}/node_modules/.cache/tsgo/declarations/${tsgoCacheKey(packagePath)}`,
413
+ `${packagePath} must emit TS-Go declarations into the generated cache`,
414
+ );
415
+ assert(
416
+ compilerOptions.tsBuildInfoFile === `${relativeRoot}/node_modules/.cache/tsgo/${tsgoCacheKey(packagePath)}.tsbuildinfo`,
417
+ `${packagePath} must keep TS-Go build info in the generated cache`,
418
+ );
419
+ };
420
+ const assertTsConfigReferenceGraph = () => {
421
+ const rootTsConfig = readJson('tsconfig.json');
422
+ const shellTsConfig = readJson('apps/shell-super-app/tsconfig.json');
423
+ const shellMfTypesTsConfig = readJson(
424
+ 'apps/shell-super-app/tsconfig.mf-types.json',
425
+ );
426
+ const expectedRootReferences = [
427
+ ...sharedPackagePaths,
428
+ 'apps/shell-super-app',
429
+ ...fullStackVerticals.map(vertical => vertical.path),
430
+ ].map(referencePath => ({ path: referencePath }));
431
+ const expectedShellReferences = [
432
+ ...sharedPackagePaths,
433
+ ...fullStackVerticals.map(vertical => vertical.path),
434
+ ].map(referencePath =>
435
+ referenceFrom('apps/shell-super-app', referencePath),
436
+ );
437
+
438
+ assertSameJson(
439
+ rootTsConfig.files,
440
+ [],
441
+ 'tsconfig.json files',
442
+ 'restore the generated root project-reference tsconfig',
443
+ );
444
+ assertSameJson(
445
+ rootTsConfig.references ?? [],
446
+ expectedRootReferences,
447
+ 'tsconfig.json references',
448
+ 'restore the generated root project-reference graph',
449
+ );
450
+ assertSameJson(
451
+ shellTsConfig.references ?? [],
452
+ expectedShellReferences,
453
+ 'apps/shell-super-app/tsconfig.json references',
454
+ 'restore the generated shell project-reference graph',
455
+ );
456
+ assertProjectReferenceEmitConfig(
457
+ shellTsConfig,
458
+ 'apps/shell-super-app',
459
+ );
460
+ assertSameJson(
461
+ shellMfTypesTsConfig,
462
+ {
463
+ extends: './tsconfig.json',
464
+ include: ['src/modern-app-env.d.ts'],
465
+ },
466
+ 'apps/shell-super-app/tsconfig.mf-types.json',
467
+ 'restore the generated shell Module Federation DTS boundary',
468
+ );
469
+ for (const sharedPackagePath of sharedPackagePaths) {
470
+ assertProjectReferenceEmitConfig(
471
+ readJson(`${sharedPackagePath}/tsconfig.json`),
472
+ sharedPackagePath,
473
+ );
474
+ }
475
+
476
+ for (const vertical of fullStackVerticals) {
477
+ const verticalTsConfig = readJson(`${vertical.path}/tsconfig.json`);
478
+ const verticalMfTypesTsConfig = readJson(
479
+ `${vertical.path}/tsconfig.mf-types.json`,
480
+ );
481
+ const expectedVerticalReferences = [
482
+ ...sharedPackagePaths,
483
+ ...(vertical.verticalRefs ?? [])
484
+ .map(verticalRef =>
485
+ fullStackVerticals.find(candidate => candidate.id === verticalRef),
486
+ )
487
+ .filter(Boolean)
488
+ .map(referencedVertical => referencedVertical.path),
489
+ ].map(referencePath => referenceFrom(vertical.path, referencePath));
490
+ assertSameJson(
491
+ verticalTsConfig.references ?? [],
492
+ expectedVerticalReferences,
493
+ `${vertical.path}/tsconfig.json references`,
494
+ 'restore the generated MicroVertical project-reference graph',
495
+ );
496
+ assertProjectReferenceEmitConfig(verticalTsConfig, vertical.path);
497
+ assertSameJson(
498
+ verticalMfTypesTsConfig,
499
+ {
500
+ extends: './tsconfig.json',
501
+ include: [
502
+ 'src/federation-entry.tsx',
503
+ ...vertical.componentPaths.map(componentPath =>
504
+ componentPath.replace(`${vertical.path}/`, ''),
505
+ ),
506
+ 'src/modern-app-env.d.ts',
507
+ ],
508
+ },
509
+ `${vertical.path}/tsconfig.mf-types.json`,
510
+ 'restore the generated MicroVertical Module Federation DTS boundary',
511
+ );
512
+ }
513
+ };
378
514
  const packageJsonFiles = startDir => {
379
515
  const files = [];
380
516
  const queue = [startDir];
@@ -511,10 +647,79 @@ const assertCloudflareQualityGates = (appId, qualityGates) => {
511
647
  assert(qualityGates?.csp?.finalMode === 'report-only-dogfood', `${appId} CSP final mode decision is missing`);
512
648
  };
513
649
  const extractAssetPrefixExpression = modernConfig => {
514
- const match = /const assetPrefix =\n(?<expression>[\s\S]*?);/u.exec(modernConfig);
650
+ const match = /const\s+assetPrefix\s*=\s*(?<expression>[\s\S]*?);/u.exec(modernConfig);
515
651
  assert(match?.groups?.expression, 'modern.config.ts must assign assetPrefix');
516
652
  return match.groups.expression;
517
653
  };
654
+ const stripYamlInlineComment = value => {
655
+ let quote = undefined;
656
+ for (let index = 0; index < value.length; index += 1) {
657
+ const character = value[index];
658
+ if (quote) {
659
+ if (character === quote && value[index - 1] !== '\\') {
660
+ quote = undefined;
661
+ }
662
+ continue;
663
+ }
664
+ if (character === '"' || character === "'") {
665
+ quote = character;
666
+ continue;
667
+ }
668
+ if (character === '#' && (index === 0 || /\s/u.test(value[index - 1]))) {
669
+ return value.slice(0, index).trimEnd();
670
+ }
671
+ }
672
+ return value.trimEnd();
673
+ };
674
+ const normalizeYamlScalar = value => {
675
+ const trimmed = stripYamlInlineComment(value).trim();
676
+ if (
677
+ ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
678
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) &&
679
+ trimmed.length >= 2
680
+ ) {
681
+ return trimmed.slice(1, -1);
682
+ }
683
+ return trimmed;
684
+ };
685
+ const extractWorkflowNodeVersions = workflowText => {
686
+ const versions = [];
687
+ const lines = workflowText.split(/\r?\n/u);
688
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
689
+ const match = /^(?<indent>\s*)node-version\s*:\s*(?<value>.*)$/u.exec(
690
+ lines[lineIndex],
691
+ );
692
+ if (!match?.groups) {
693
+ continue;
694
+ }
695
+
696
+ let value = normalizeYamlScalar(match.groups.value);
697
+ if (value === '' || value === '|' || value === '>') {
698
+ const currentIndent = match.groups.indent.length;
699
+ for (
700
+ let nextLineIndex = lineIndex + 1;
701
+ nextLineIndex < lines.length;
702
+ nextLineIndex += 1
703
+ ) {
704
+ const nextLine = lines[nextLineIndex];
705
+ const nextTrimmed = nextLine.trim();
706
+ if (nextTrimmed === '' || nextTrimmed.startsWith('#')) {
707
+ continue;
708
+ }
709
+ const nextIndent = nextLine.match(/^\s*/u)?.[0]?.length ?? 0;
710
+ if (nextIndent <= currentIndent) {
711
+ break;
712
+ }
713
+ value = normalizeYamlScalar(nextTrimmed);
714
+ break;
715
+ }
716
+ }
717
+ if (value !== '') {
718
+ versions.push(value);
719
+ }
720
+ }
721
+ return versions;
722
+ };
518
723
  const expectedWorkerName = packageSuffix => `${packageScope}-${packageSuffix}`.slice(0, 63);
519
724
  const expectedChunkLoadingGlobal = mfName =>
520
725
  `__ULTRAMODERN_${mfName
@@ -559,6 +764,7 @@ const requiredPaths = [
559
764
  '.gitignore',
560
765
  'package.json',
561
766
  'pnpm-workspace.yaml',
767
+ 'tsconfig.json',
562
768
  'tsconfig.base.json',
563
769
  'oxlint.config.ts',
564
770
  'oxfmt.config.ts',
@@ -581,7 +787,10 @@ const requiredPaths = [
581
787
  'scripts/setup-agent-reference-repos.mjs',
582
788
  'scripts/ultramodern-performance-readiness.config.mjs',
583
789
  'scripts/ultramodern-performance-readiness.mjs',
790
+ 'scripts/ultramodern-typecheck.mjs',
584
791
  'apps/shell-super-app/package.json',
792
+ 'apps/shell-super-app/tsconfig.json',
793
+ 'apps/shell-super-app/tsconfig.mf-types.json',
585
794
  'apps/shell-super-app/modern.config.ts',
586
795
  'apps/shell-super-app/module-federation.config.ts',
587
796
  'apps/shell-super-app/src/modern-app-env.d.ts',
@@ -597,9 +806,13 @@ const requiredPaths = [
597
806
  'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
598
807
  'apps/shell-super-app/src/routes/[lang]/page.tsx',
599
808
  ...{{shellRouteMetaPathsJson}},
809
+ 'packages/shared-contracts/package.json',
600
810
  'packages/shared-contracts/src/index.ts',
811
+ 'packages/shared-contracts/tsconfig.json',
812
+ 'packages/shared-design-tokens/package.json',
601
813
  'packages/shared-design-tokens/src/index.ts',
602
814
  'packages/shared-design-tokens/src/tokens.css',
815
+ 'packages/shared-design-tokens/tsconfig.json',
603
816
  ];
604
817
 
605
818
  for (const vertical of fullStackVerticals) {
@@ -643,6 +856,7 @@ assertMicroVerticalContractGraph({
643
856
  overlay,
644
857
  shellPackage,
645
858
  });
859
+ assertTsConfigReferenceGraph();
646
860
 
647
861
  assert(rootPackage.private === true, 'Root package must be private');
648
862
  assert(typeof rootPackage.packageManager === 'string', 'Root must declare packageManager');
@@ -660,7 +874,12 @@ assert(generatedContract.node?.engineRange === '>=26', 'Generated contract must
660
874
  assert(readText('.mise.toml').includes(`node = "${expectedNodeVersion}"`), 'mise must pin the generated Node version');
661
875
  assert(readText('.mise.toml').includes(`pnpm = "${packageManagerPnpmVersion}"`), 'mise must pin the generated pnpm version');
662
876
  const workflowText = readText('.github/workflows/ultramodern-workspace-gates.yml');
663
- assert(workflowText.includes(`node-version: "${expectedNodeVersion}"`), 'CI workflow must pin the generated Node version');
877
+ const workflowNodeVersions = extractWorkflowNodeVersions(workflowText);
878
+ assert(workflowNodeVersions.length > 0, 'CI workflow must configure setup-node node-version');
879
+ assert(
880
+ workflowNodeVersions.every(nodeVersion => nodeVersion === expectedNodeVersion),
881
+ `CI workflow must pin the generated Node version ${expectedNodeVersion}; found ${workflowNodeVersions.join(', ')}`,
882
+ );
664
883
  assert(!workflowText.includes('FORCE_JAVASCRIPT_ACTIONS_TO_NODE24'), 'CI workflow must not carry the legacy Node 24 override');
665
884
  assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
666
885
  assert(rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json', 'Root must point at package source metadata');
@@ -711,6 +930,7 @@ assert(
711
930
  );
712
931
  assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
713
932
  assert(!('ultramodern:check' in (rootPackage.scripts ?? {})), 'Root must not expose ultramodern:check');
933
+ assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mjs --build tsconfig.json', 'Root typecheck must run TS-Go build mode across project references');
714
934
  assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultramodern-workspace.mjs', 'Root must expose contract:check');
715
935
  assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mjs', 'Root must expose i18n:boundaries');
716
936
  assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mjs', 'Root must expose default-on performance readiness diagnostics');
@@ -723,6 +943,9 @@ assert(performanceReadinessConfig.includes("failOn: 'framework-invariant'"), 'Pe
723
943
  assert(performanceReadinessScript.includes("ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS"), 'Performance readiness script must support env opt-out');
724
944
  assert(performanceReadinessScript.includes('ultramodern-performance-readiness-diagnostics-v1'), 'Performance readiness script must emit the deterministic report profile');
725
945
  const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mjs');
946
+ const ultramodernTypecheckScript = readText('scripts/ultramodern-typecheck.mjs');
947
+ assert(ultramodernTypecheckScript.includes("'--checkers'") && ultramodernTypecheckScript.includes("'--builders'"), 'Root typecheck script must drive TS-Go checker and builder parallelism');
948
+ assert(ultramodernTypecheckScript.includes('effect-tsgo') && ultramodernTypecheckScript.includes('get-exe-path'), 'Root typecheck script must resolve the @effect/tsgo native-preview binary');
726
949
  assert(
727
950
  i18nBoundaryScript.includes("from '@modern-js/code-tools'") &&
728
951
  i18nBoundaryScript.includes('runWorkspaceSourceCheck'),
@@ -775,6 +998,7 @@ assert(generatedContract.performanceReadiness?.optOut?.env === 'ULTRAMODERN_PERF
775
998
  assert(JSON.stringify(generatedContract.performanceReadiness?.signals?.map(signal => signal.id)) === JSON.stringify(expectedPerformanceReadinessSignals), 'Performance readiness signal ids are incorrect');
776
999
 
777
1000
  const shellModernConfig = readText('apps/shell-super-app/modern.config.ts');
1001
+ const shellModuleFederationConfig = readText('apps/shell-super-app/module-federation.config.ts');
778
1002
  const shellRouteHead = readText('apps/shell-super-app/src/routes/ultramodern-route-head.tsx');
779
1003
  const shellRouteMetadata = readText('apps/shell-super-app/src/routes/ultramodern-route-metadata.ts');
780
1004
  assert(shellRouteMetadata.includes('@generated by @modern-js/create'), 'Shell route metadata compatibility manifest must be marked generated');
@@ -819,12 +1043,16 @@ assert(shellModernConfig.includes('assetPrefix,'), 'Shell modern.config.ts must
819
1043
  assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
820
1044
  assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
821
1045
  assert(JSON.stringify(shellContract?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), 'Shell asset prefix env fallback order is incorrect');
1046
+ assert(shellContract?.config?.output?.disableTsChecker === false, 'Shell must keep the framework TypeScript checker enabled');
822
1047
  assert(shellContract?.config?.performance?.readinessDiagnostics?.default === 'enabled', 'Shell performance readiness diagnostics must be default-on');
823
1048
  assert(shellContract?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', 'Shell performance readiness diagnostics must only fail framework invariants by default');
824
1049
  assert(shellContract?.config?.performance?.readinessDiagnostics?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Shell performance readiness env opt-out is incorrect');
825
1050
  assert(JSON.stringify(shellContract?.config?.source?.siteUrl?.envFallbackOrder) === JSON.stringify(['MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN', 'SHELL_SUPER_APP_PORT']), 'Shell site URL env fallback order is incorrect');
826
1051
  assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
827
1052
  assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
1053
+ assert(shellContract?.moduleFederation?.dts?.compilerInstance === 'tsgo', 'Shell must keep mandatory DTS compiler');
1054
+ assert(shellContract?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', 'Shell must keep dedicated Module Federation DTS tsconfig');
1055
+ assert(shellModuleFederationConfig.includes("tsConfigPath: './tsconfig.mf-types.json'"), 'Shell Module Federation config must use the dedicated DTS tsconfig');
828
1056
  assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
829
1057
  assert(shellContract?.styling?.federation?.owner?.id === 'shell-super-app', 'Shell CSS federation owner is missing');
830
1058
  assert(shellContract?.styling?.federation?.role === 'shell-base-overlay', 'Shell must own base and overlay CSS');
@@ -859,6 +1087,7 @@ assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, no
859
1087
  for (const vertical of fullStackVerticals) {
860
1088
  const packageJson = readJson(`${vertical.path}/package.json`);
861
1089
  const modernConfig = readText(`${vertical.path}/modern.config.ts`);
1090
+ const moduleFederationConfig = readText(`${vertical.path}/module-federation.config.ts`);
862
1091
  const routeHead = readText(`${vertical.path}/src/routes/ultramodern-route-head.tsx`);
863
1092
  const routeMetadata = readText(`${vertical.path}/src/routes/ultramodern-route-metadata.ts`);
864
1093
  assert(routeMetadata.includes('@generated by @modern-js/create'), `${vertical.id} route metadata compatibility manifest must be marked generated`);
@@ -913,6 +1142,7 @@ for (const vertical of fullStackVerticals) {
913
1142
  assert(contractEntry?.config?.dev?.assetPrefix === '/', `${vertical.id} dev asset prefix must stay origin-relative`);
914
1143
  assert(contractEntry?.config?.output?.assetPrefix?.default === '/', `${vertical.id} asset prefix must default to origin-relative paths`);
915
1144
  assert(JSON.stringify(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), `${vertical.id} asset prefix env fallback order is incorrect`);
1145
+ assert(contractEntry?.config?.output?.disableTsChecker === false, `${vertical.id} must keep the framework TypeScript checker enabled`);
916
1146
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.default === 'enabled', `${vertical.id} performance readiness diagnostics must be default-on`);
917
1147
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', `${vertical.id} performance readiness diagnostics must only fail framework invariants by default`);
918
1148
  assert(contractEntry?.config?.performance?.readinessDiagnostics?.optOut?.config === 'scripts/ultramodern-performance-readiness.config.mjs', `${vertical.id} performance readiness opt-out config is incorrect`);
@@ -922,6 +1152,8 @@ for (const vertical of fullStackVerticals) {
922
1152
  assert(contractEntry?.moduleFederation?.name === vertical.mfName, `${vertical.id} MF name is incorrect`);
923
1153
  assert(JSON.stringify(contractEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), `${vertical.id} MF exposes are incorrect`);
924
1154
  assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'tsgo', `${vertical.id} must keep mandatory DTS compiler`);
1155
+ assert(contractEntry?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', `${vertical.id} must keep dedicated Module Federation DTS tsconfig`);
1156
+ assert(moduleFederationConfig.includes("tsConfigPath: './tsconfig.mf-types.json'"), `${vertical.id} Module Federation config must use the dedicated DTS tsconfig`);
925
1157
  assert(JSON.stringify(contractEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), `${vertical.id} MF verticalRefs are incorrect`);
926
1158
  assert(
927
1159
  JSON.stringify((contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id)) ===