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

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 (79) hide show
  1. package/dist/cjs/ultramodern-tooling/commands.cjs +497 -155
  2. package/dist/cjs/ultramodern-tooling/config.cjs +149 -8
  3. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +25 -0
  4. package/dist/cjs/ultramodern-workspace/api.cjs +44 -0
  5. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +282 -0
  6. package/dist/cjs/ultramodern-workspace/contracts.cjs +24 -8
  7. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +157 -0
  8. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +88 -0
  9. package/dist/cjs/ultramodern-workspace/descriptors.cjs +24 -0
  10. package/dist/cjs/ultramodern-workspace/module-federation.cjs +122 -44
  11. package/dist/cjs/ultramodern-workspace/package-json.cjs +11 -34
  12. package/dist/cjs/ultramodern-workspace/pnpm-workspace-policy-plan.cjs +166 -0
  13. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +153 -0
  14. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +175 -0
  15. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +43 -62
  16. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +97 -0
  17. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +9 -3
  18. package/dist/cjs/ultramodern-workspace/zerops.cjs +100 -0
  19. package/dist/esm/ultramodern-tooling/commands.js +500 -158
  20. package/dist/esm/ultramodern-tooling/config.js +144 -9
  21. package/dist/esm/ultramodern-workspace/add-vertical.js +26 -1
  22. package/dist/esm/ultramodern-workspace/api.js +43 -2
  23. package/dist/esm/ultramodern-workspace/backend-federation.js +217 -0
  24. package/dist/esm/ultramodern-workspace/contracts.js +24 -8
  25. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +108 -0
  26. package/dist/esm/ultramodern-workspace/delivery-unit.js +31 -0
  27. package/dist/esm/ultramodern-workspace/descriptors.js +13 -1
  28. package/dist/esm/ultramodern-workspace/module-federation.js +121 -36
  29. package/dist/esm/ultramodern-workspace/package-json.js +11 -31
  30. package/dist/esm/ultramodern-workspace/pnpm-workspace-policy-plan.js +113 -0
  31. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
  32. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +110 -0
  33. package/dist/esm/ultramodern-workspace/workspace-scripts.js +35 -63
  34. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +59 -0
  35. package/dist/esm/ultramodern-workspace/write-workspace.js +11 -5
  36. package/dist/esm/ultramodern-workspace/zerops.js +62 -0
  37. package/dist/esm-node/ultramodern-tooling/commands.js +500 -158
  38. package/dist/esm-node/ultramodern-tooling/config.js +144 -9
  39. package/dist/esm-node/ultramodern-workspace/add-vertical.js +26 -1
  40. package/dist/esm-node/ultramodern-workspace/api.js +43 -2
  41. package/dist/esm-node/ultramodern-workspace/backend-federation.js +218 -0
  42. package/dist/esm-node/ultramodern-workspace/contracts.js +24 -8
  43. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +109 -0
  44. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +32 -0
  45. package/dist/esm-node/ultramodern-workspace/descriptors.js +13 -1
  46. package/dist/esm-node/ultramodern-workspace/module-federation.js +121 -36
  47. package/dist/esm-node/ultramodern-workspace/package-json.js +11 -31
  48. package/dist/esm-node/ultramodern-workspace/pnpm-workspace-policy-plan.js +114 -0
  49. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
  50. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +111 -0
  51. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +35 -63
  52. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +60 -0
  53. package/dist/esm-node/ultramodern-workspace/write-workspace.js +11 -5
  54. package/dist/esm-node/ultramodern-workspace/zerops.js +63 -0
  55. package/dist/types/ultramodern-tooling/config.d.ts +6 -0
  56. package/dist/types/ultramodern-workspace/api.d.ts +1 -0
  57. package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
  58. package/dist/types/ultramodern-workspace/contracts.d.ts +1 -1
  59. package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
  60. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +27 -0
  61. package/dist/types/ultramodern-workspace/descriptors.d.ts +4 -0
  62. package/dist/types/ultramodern-workspace/module-federation.d.ts +4 -9
  63. package/dist/types/ultramodern-workspace/package-json.d.ts +0 -1
  64. package/dist/types/ultramodern-workspace/pnpm-workspace-policy-plan.d.ts +39 -0
  65. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
  66. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +72 -0
  67. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +3 -0
  68. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +42 -0
  69. package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
  70. package/package.json +3 -3
  71. package/template-workspace/.gitignore.handlebars +1 -0
  72. package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
  73. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
  74. package/templates/workspace-scripts/generate-node-backend-federation.mjs +270 -0
  75. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +448 -0
  76. package/templates/workspace-scripts/proof-cloudflare-version.mjs +234 -8
  77. package/templates/workspace-scripts/proof-node-backend-federation.mjs +729 -0
  78. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
  79. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +322 -8
@@ -0,0 +1,100 @@
1
+ const defineToolingCommand = (command)=>({
2
+ ...command,
3
+ wrapperPath: `scripts/${command.wrapperName}.mts`
4
+ });
5
+ const generatedToolingCommands = [
6
+ defineToolingCommand({
7
+ id: 'validate',
8
+ command: 'validate',
9
+ wrapperName: 'validate-ultramodern-workspace',
10
+ contractKey: 'validate',
11
+ rootScript: 'contract:check'
12
+ }),
13
+ defineToolingCommand({
14
+ id: 'typecheck',
15
+ command: 'typecheck',
16
+ wrapperName: 'ultramodern-typecheck',
17
+ contractKey: 'typecheck',
18
+ rootScript: 'typecheck',
19
+ templatePath: "templates/workspace-scripts/ultramodern-typecheck.mjs",
20
+ cwd: 'invocation'
21
+ }),
22
+ defineToolingCommand({
23
+ id: 'mfTypes',
24
+ command: 'mf-types',
25
+ wrapperName: 'assert-mf-types',
26
+ contractKey: 'mfTypes',
27
+ rootScript: 'mf:types'
28
+ }),
29
+ defineToolingCommand({
30
+ id: 'publicSurface',
31
+ command: 'public-surface',
32
+ wrapperName: 'generate-public-surface-assets',
33
+ contractKey: 'publicSurface',
34
+ templatePath: "templates/workspace-scripts/generate-public-surface-assets.mjs"
35
+ }),
36
+ defineToolingCommand({
37
+ id: 'backendFederationGenerate',
38
+ command: 'backend-federation-generate',
39
+ wrapperName: 'generate-node-backend-federation',
40
+ contractKey: 'backendFederationGenerate',
41
+ rootScript: 'node:backend-federation:generate',
42
+ templatePath: "templates/workspace-scripts/generate-node-backend-federation.mjs"
43
+ }),
44
+ defineToolingCommand({
45
+ id: 'backendFederationProof',
46
+ command: 'backend-federation-proof',
47
+ wrapperName: 'proof-node-backend-federation',
48
+ contractKey: 'backendFederationProof',
49
+ rootScript: 'node:proof',
50
+ templatePath: "templates/workspace-scripts/proof-node-backend-federation.mjs"
51
+ }),
52
+ defineToolingCommand({
53
+ id: 'cloudflareProof',
54
+ command: 'cloudflare-proof',
55
+ wrapperName: 'proof-cloudflare-version',
56
+ contractKey: 'cloudflareProof',
57
+ rootScript: 'cloudflare:proof',
58
+ templatePath: "templates/workspace-scripts/proof-cloudflare-version.mjs"
59
+ }),
60
+ defineToolingCommand({
61
+ id: 'cloudflareOutputVerify',
62
+ command: 'cloudflare-output-verify',
63
+ wrapperName: 'verify-cloudflare-output',
64
+ contractKey: 'cloudflareOutputVerify',
65
+ rootScript: 'cloudflare-output:verify'
66
+ }),
67
+ defineToolingCommand({
68
+ id: 'performanceReadiness',
69
+ command: 'performance-readiness',
70
+ wrapperName: 'ultramodern-performance-readiness',
71
+ contractKey: 'performanceReadiness',
72
+ rootScript: 'performance:readiness',
73
+ templatePath: "templates/workspace-scripts/ultramodern-performance-readiness.mjs"
74
+ }),
75
+ defineToolingCommand({
76
+ id: 'migrateStrictEffect',
77
+ command: 'migrate-strict-effect',
78
+ wrapperName: 'migrate-strict-effect',
79
+ contractKey: 'migrateStrictEffect',
80
+ rootScript: 'migrate:strict-effect'
81
+ }),
82
+ defineToolingCommand({
83
+ id: 'routesGenerate',
84
+ command: 'routes-generate',
85
+ wrapperName: 'generate-tanstack-routes',
86
+ contractKey: 'routesGenerate'
87
+ })
88
+ ];
89
+ const toolingCommandById = Object.fromEntries(generatedToolingCommands.map((command)=>[
90
+ command.id,
91
+ command
92
+ ]));
93
+ const GENERATED_TOOLING_COMMANDS = toolingCommandById;
94
+ const generatedToolingCommandList = ()=>generatedToolingCommands.map((command)=>command.command);
95
+ const createToolingWrapperContract = ()=>Object.fromEntries(generatedToolingCommands.map((command)=>[
96
+ command.contractKey,
97
+ command.wrapperPath
98
+ ]));
99
+ const createGeneratedToolingWrapperMap = createToolingWrapperContract;
100
+ export { GENERATED_TOOLING_COMMANDS, createGeneratedToolingWrapperMap, createToolingWrapperContract, generatedToolingCommandList, generatedToolingCommands, toolingCommandById };
@@ -0,0 +1,110 @@
1
+ import { appHasApi } from "./descriptors.js";
2
+ import { relativeRootFor } from "./naming.js";
3
+ import { createPublicSurfaceGenerationCommand } from "./public-surface.js";
4
+ import { GENERATED_TOOLING_COMMANDS } from "./tooling-command-catalog.js";
5
+ const toolingWrapperPath = (key)=>GENERATED_TOOLING_COMMANDS[key].wrapperPath;
6
+ const rootToolingScriptName = (key)=>{
7
+ const rootScript = GENERATED_TOOLING_COMMANDS[key].rootScript;
8
+ if (!rootScript) throw new Error(`Generated tooling command ${key} does not define a root package script.`);
9
+ return rootScript;
10
+ };
11
+ const rootToolingWrapperCommand = (key)=>`node ./${toolingWrapperPath(key)}`;
12
+ const relativeToolingWrapperPath = (packageDir, key)=>`${relativeRootFor(packageDir)}/${toolingWrapperPath(key)}`;
13
+ const packageToolingWrapperCommand = (packageDir, key)=>`node ${relativeToolingWrapperPath(packageDir, key)}`;
14
+ const workspaceRootPackageScriptNames = {
15
+ build: 'build',
16
+ cloudflareBuild: 'cloudflare:build',
17
+ cloudflareDeploy: 'cloudflare:deploy',
18
+ cloudflareProof: rootToolingScriptName('cloudflareProof'),
19
+ cloudflareOutputVerify: rootToolingScriptName('cloudflareOutputVerify'),
20
+ backendFederationGenerate: rootToolingScriptName('backendFederationGenerate'),
21
+ nodeProof: rootToolingScriptName('backendFederationProof'),
22
+ mfTypes: rootToolingScriptName('mfTypes'),
23
+ performanceReadiness: rootToolingScriptName('performanceReadiness'),
24
+ migrateStrictEffect: rootToolingScriptName('migrateStrictEffect'),
25
+ zeropsMaterialize: 'zerops:materialize',
26
+ contractCheck: rootToolingScriptName('validate'),
27
+ typecheck: 'typecheck',
28
+ check: 'check'
29
+ };
30
+ const workspaceAppPackageScriptNames = {
31
+ dev: 'dev',
32
+ build: 'build',
33
+ cloudflareBuild: 'cloudflare:build',
34
+ cloudflareDeploy: 'cloudflare:deploy',
35
+ cloudflarePreview: 'cloudflare:preview',
36
+ cloudflareProof: 'cloudflare:proof',
37
+ serve: 'serve',
38
+ typecheck: 'typecheck'
39
+ };
40
+ const createStrictTsgoTypecheckCommand = (packageDir)=>`${packageToolingWrapperCommand(packageDir, 'typecheck')} --project tsconfig.json`;
41
+ function createWorkspaceAppScriptPlan(app) {
42
+ const backendFederationBuildCommand = appHasApi(app) ? `${packageToolingWrapperCommand(app.directory, 'backendFederationGenerate')} --app ${app.id}` : void 0;
43
+ const backendFederationCloudflareBuildCommand = backendFederationBuildCommand ? `${backendFederationBuildCommand} --target dist-cloudflare` : void 0;
44
+ const buildSteps = [
45
+ 'ULTRAMODERN_ZEPHYR=false modern build',
46
+ backendFederationBuildCommand,
47
+ createPublicSurfaceGenerationCommand(app, 'dist'),
48
+ app.exposes ? packageToolingWrapperCommand(app.directory, 'mfTypes') : void 0
49
+ ].filter((step)=>Boolean(step));
50
+ const cloudflareBuildSteps = [
51
+ 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build',
52
+ backendFederationCloudflareBuildCommand,
53
+ 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy --skip-build',
54
+ createPublicSurfaceGenerationCommand(app, 'cloudflare'),
55
+ `${packageToolingWrapperCommand(app.directory, 'cloudflareOutputVerify')} --app ${app.id}`
56
+ ].filter((step)=>Boolean(step));
57
+ return {
58
+ dev: 'modern dev',
59
+ build: buildSteps.join(' && '),
60
+ cloudflareBuild: cloudflareBuildSteps.join(' && '),
61
+ cloudflareDeploy: 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json',
62
+ cloudflarePreview: 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
63
+ cloudflareProof: `${packageToolingWrapperCommand(app.directory, 'cloudflareProof')} --app ${app.id}`,
64
+ serve: 'modern serve',
65
+ typecheck: createStrictTsgoTypecheckCommand(app.directory)
66
+ };
67
+ }
68
+ function createWorkspaceAppPackageScripts(app) {
69
+ const plan = createWorkspaceAppScriptPlan(app);
70
+ return Object.fromEntries(Object.entries(workspaceAppPackageScriptNames).map(([planKey, packageScriptName])=>[
71
+ packageScriptName,
72
+ plan[planKey]
73
+ ]));
74
+ }
75
+ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
76
+ const hasRemotes = remotes.length > 0;
77
+ const mfTypesScript = rootToolingScriptName('mfTypes');
78
+ const performanceReadinessScript = rootToolingScriptName('performanceReadiness');
79
+ const cloudflareOutputVerifyScript = rootToolingScriptName('cloudflareOutputVerify');
80
+ const backendFederationGenerateScript = rootToolingScriptName('backendFederationGenerate');
81
+ const nodeProofScript = rootToolingScriptName('backendFederationProof');
82
+ const bridgeCheck = options.bridgeCheck ?? '';
83
+ const remoteBuildPrefix = hasRemotes ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ' : '';
84
+ const remoteCloudflareBuildPrefix = hasRemotes ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && ' : '';
85
+ const remoteCloudflareDeployPrefix = hasRemotes ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && ' : '';
86
+ return {
87
+ build: `${remoteBuildPrefix}ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm ${mfTypesScript} && pnpm ${performanceReadinessScript}`,
88
+ cloudflareBuild: `${remoteCloudflareBuildPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ${mfTypesScript} && pnpm ${cloudflareOutputVerifyScript}`,
89
+ cloudflareDeploy: `${remoteCloudflareDeployPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:deploy`,
90
+ cloudflareProof: `${rootToolingWrapperCommand('cloudflareProof')} --out .codex/reports/cloudflare-version-proof/public-url-proof.json`,
91
+ cloudflareOutputVerify: rootToolingWrapperCommand('cloudflareOutputVerify'),
92
+ backendFederationGenerate: rootToolingWrapperCommand('backendFederationGenerate'),
93
+ nodeProof: rootToolingWrapperCommand('backendFederationProof'),
94
+ mfTypes: rootToolingWrapperCommand('mfTypes'),
95
+ performanceReadiness: rootToolingWrapperCommand('performanceReadiness'),
96
+ migrateStrictEffect: rootToolingWrapperCommand('migrateStrictEffect'),
97
+ zeropsMaterialize: "node ./scripts/materialize-zerops-runtime.mjs",
98
+ contractCheck: rootToolingWrapperCommand('validate'),
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}`
101
+ };
102
+ }
103
+ function createWorkspaceRootPackageScripts(remotes = [], options = {}) {
104
+ const plan = createWorkspaceRootScriptPlan(remotes, options);
105
+ return Object.fromEntries(Object.entries(workspaceRootPackageScriptNames).map(([planKey, packageScriptName])=>[
106
+ packageScriptName,
107
+ plan[planKey]
108
+ ]));
109
+ }
110
+ export { createStrictTsgoTypecheckCommand, createWorkspaceAppPackageScripts, createWorkspaceAppScriptPlan, createWorkspaceRootPackageScripts, createWorkspaceRootScriptPlan, packageToolingWrapperCommand, rootToolingScriptName, rootToolingWrapperCommand, workspaceAppPackageScriptNames, workspaceRootPackageScriptNames };
@@ -1,14 +1,8 @@
1
1
  import node_fs from "node:fs";
2
2
  import node_path from "node:path";
3
- import { verticalApiGroupName } from "./api.js";
4
- import { remoteComponentOutputPath } from "./demo-components.js";
5
- import { appHasApi, appI18nNamespace, remoteDependencyAlias, shellApp } from "./descriptors.js";
6
3
  import { readFileTemplate, renderFileTemplate, writeFileReplacing } from "./fs-io.js";
7
- import { packageName, tailwindPrefixForApp } from "./naming.js";
8
- import { createCloudflareSecurityContract } from "./policy.js";
9
- import { publicSurfaceManagedSourceAssetPaths } from "./public-surface.js";
10
- import { createLocalisedUrlsMap, createRouteMetaFilePath, createRouteOwnedI18nPaths, createRoutePageFilePath } from "./routes.js";
11
- import { CLOUDFLARE_COMPATIBILITY_DATE, EFFECT_VERSION, MODULE_FEDERATION_VERSION, NODE_VERSION, PNPM_VERSION } from "./versions.js";
4
+ import { GENERATED_TOOLING_COMMANDS, generatedToolingCommands } from "./tooling-command-catalog.js";
5
+ import { createWorkspaceValidationContract } from "./workspace-validation-contract.js";
12
6
  function createToolWrapperScript(command, extraArgs = []) {
13
7
  const commandJson = JSON.stringify(command);
14
8
  const extraArgsJson = JSON.stringify(extraArgs);
@@ -42,6 +36,13 @@ if (result.error) {
42
36
  process.exit(result.status ?? 1);
43
37
  `;
44
38
  }
39
+ function writeGeneratedToolWrapperScript(targetDir, key) {
40
+ const command = GENERATED_TOOLING_COMMANDS[key];
41
+ writeWorkspaceOwnedMtsScript(targetDir, command.wrapperName, createToolWrapperScript(command.command));
42
+ }
43
+ function writeGeneratedToolWrapperScripts(targetDir) {
44
+ for (const command of generatedToolingCommands)writeGeneratedToolWrapperScript(targetDir, command.id);
45
+ }
45
46
  function createSkillsToolWrapperScript() {
46
47
  return `#!/usr/bin/env node
47
48
  import { spawnSync } from 'node:child_process';
@@ -96,54 +97,24 @@ function migrateCopiedWorkspaceScriptToMts(targetDir, name) {
96
97
  node_fs.renameSync(legacyPath, migratedPath);
97
98
  }
98
99
  function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
99
- const verticals = remotes.filter(appHasApi).map((remote)=>({
100
- id: remote.id,
101
- domain: remote.domain,
102
- stem: remote.api.stem,
103
- group: verticalApiGroupName(remote),
104
- path: remote.directory,
105
- port: remote.port,
106
- mfName: remote.mfName,
107
- apiPrefix: remote.api.prefix,
108
- tailwindPrefix: tailwindPrefixForApp(remote),
109
- zephyrAlias: remoteDependencyAlias(remote),
110
- packageName: packageName(scope, remote.packageSuffix),
111
- exposes: Object.keys(remote.exposes ?? {}),
112
- componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
113
- namespace: appI18nNamespace(remote),
114
- routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
115
- routeMetaPaths: createRouteOwnedI18nPaths(remote).map((route)=>createRouteMetaFilePath(remote, route.canonicalPath)),
116
- localisedUrls: createLocalisedUrlsMap(remote),
117
- verticalRefs: remote.verticalRefs ?? []
118
- }));
119
- const shellRouteMetaPaths = createRouteOwnedI18nPaths(shellApp).map((route)=>createRouteMetaFilePath(shellApp, route.canonicalPath));
120
- const shellNamespace = appI18nNamespace(shellApp);
121
- const oldRemotePaths = [
122
- 'apps/remotes'
123
- ];
124
- const expectedBuildScript = remotes.length > 0 ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types && pnpm performance:readiness' : 'ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types && pnpm performance:readiness';
125
- const expectedCloudflareBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types';
126
- const expectedCloudflareDeployScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && pnpm --filter "./apps/shell-super-app" run cloudflare:deploy' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:deploy';
127
- const expectedCloudflareSecurity = createCloudflareSecurityContract();
100
+ const contract = createWorkspaceValidationContract(scope, enableTailwind, remotes);
128
101
  return renderFileTemplate("workspace-scripts/validate-ultramodern-workspace.mjs", {
129
- packageScope: scope,
130
- effectVersion: EFFECT_VERSION,
131
- moduleFederationVersion: MODULE_FEDERATION_VERSION,
132
- nodeVersion: NODE_VERSION,
133
- pnpmVersion: PNPM_VERSION,
134
- tailwindEnabledJson: JSON.stringify(enableTailwind),
135
- fullStackVerticalsJson: JSON.stringify(verticals, null, 2),
136
- shellNamespaceJson: JSON.stringify(shellNamespace),
137
- oldRemotePathsJson: JSON.stringify(oldRemotePaths, null, 2),
138
- expectedBuildScriptJson: JSON.stringify(expectedBuildScript),
139
- expectedCloudflareBuildScriptJson: JSON.stringify(expectedCloudflareBuildScript),
140
- expectedCloudflareDeployScriptJson: JSON.stringify(expectedCloudflareDeployScript),
141
- expectedCloudflareSecurityJson: JSON.stringify(expectedCloudflareSecurity, null, 2),
142
- publicSurfaceManagedSourceAssetPathsJson: JSON.stringify([
143
- ...publicSurfaceManagedSourceAssetPaths
144
- ], null, 2),
145
- shellRouteMetaPathsJson: JSON.stringify(shellRouteMetaPaths, null, 2),
146
- cloudflareCompatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
102
+ packageScope: contract.packageScope,
103
+ nodeVersion: contract.versions.node,
104
+ tailwindEnabledJson: JSON.stringify(contract.tailwindEnabled),
105
+ fullStackVerticalsJson: JSON.stringify(contract.fullStackVerticals, null, 2),
106
+ shellNamespaceJson: JSON.stringify(contract.shellNamespace),
107
+ oldRemotePathsJson: JSON.stringify(contract.oldRemotePaths),
108
+ expectedBuildScriptJson: JSON.stringify(contract.scripts.build),
109
+ expectedCloudflareBuildScriptJson: JSON.stringify(contract.scripts.cloudflareBuild),
110
+ expectedCloudflareDeployScriptJson: JSON.stringify(contract.scripts.cloudflareDeploy),
111
+ expectedCloudflareSecurityJson: JSON.stringify(contract.cloudflareSecurity, null, 2),
112
+ workspaceValidationContractJson: JSON.stringify(contract, null, 2),
113
+ publicSurfaceManagedSourceAssetPathsJson: JSON.stringify(contract.publicSurfaceManagedSourceAssetPaths, null, 2),
114
+ shellRouteMetaPathsJson: JSON.stringify(contract.shellRouteMetaPaths, null, 2),
115
+ effectVersion: contract.versions.effect,
116
+ moduleFederationVersion: contract.versions.moduleFederation,
117
+ cloudflareCompatibilityDate: contract.versions.cloudflareCompatibilityDate
147
118
  });
148
119
  }
149
120
  function createWorkspaceI18nBoundaryValidationScript() {
@@ -155,18 +126,19 @@ function createWorkspaceApiBoundaryValidationScript() {
155
126
  function createPerformanceReadinessConfigScript() {
156
127
  return readFileTemplate("workspace-scripts/ultramodern-performance-readiness.config.mjs");
157
128
  }
129
+ function createNodeBackendFederationProofScript() {
130
+ return readFileTemplate("workspace-scripts/proof-node-backend-federation.mjs");
131
+ }
132
+ function createZeropsRuntimeMaterializationScript() {
133
+ return readFileTemplate("workspace-scripts/materialize-zerops-runtime.mjs");
134
+ }
158
135
  function writeGeneratedWorkspaceScripts(targetDir, _scope, _enableTailwind, _remotes = []) {
159
- writeWorkspaceOwnedMtsScript(targetDir, 'assert-mf-types', createToolWrapperScript('mf-types'));
160
- writeWorkspaceOwnedMtsScript(targetDir, 'validate-ultramodern-workspace', createToolWrapperScript('validate'));
161
136
  writeWorkspaceOwnedMtsScript(targetDir, 'check-ultramodern-i18n-boundaries', createWorkspaceI18nBoundaryValidationScript());
162
137
  writeWorkspaceOwnedMtsScript(targetDir, 'check-ultramodern-api-boundaries', createWorkspaceApiBoundaryValidationScript());
163
- writeWorkspaceOwnedMtsScript(targetDir, 'generate-public-surface-assets', createToolWrapperScript('public-surface'));
164
- writeWorkspaceOwnedMtsScript(targetDir, 'proof-cloudflare-version', createToolWrapperScript('cloudflare-proof'));
138
+ writeFileReplacing(targetDir, "scripts/materialize-zerops-runtime.mjs", createZeropsRuntimeMaterializationScript());
165
139
  writeFileReplacing(targetDir, "scripts/ultramodern-performance-readiness.config.mjs", createPerformanceReadinessConfigScript());
166
- writeWorkspaceOwnedMtsScript(targetDir, 'ultramodern-performance-readiness', createToolWrapperScript('performance-readiness'));
167
- writeWorkspaceOwnedMtsScript(targetDir, 'migrate-strict-effect', createToolWrapperScript('migrate-strict-effect'));
168
- writeWorkspaceOwnedMtsScript(targetDir, 'ultramodern-typecheck', createToolWrapperScript('typecheck'));
140
+ writeGeneratedToolWrapperScripts(targetDir);
169
141
  writeWorkspaceOwnedMtsScript(targetDir, 'bootstrap-agent-skills', createSkillsToolWrapperScript());
170
142
  migrateCopiedWorkspaceScriptToMts(targetDir, 'setup-agent-reference-repos');
171
143
  }
172
- export { createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, writeGeneratedWorkspaceScripts };
144
+ export { createNodeBackendFederationProofScript, createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, writeGeneratedToolWrapperScripts, writeGeneratedWorkspaceScripts };
@@ -0,0 +1,59 @@
1
+ import { verticalApiGroupName } from "./api.js";
2
+ import { createBackendFederationMetadata } from "./backend-federation.js";
3
+ import { createDeliveryUnitRecord } from "./delivery-unit.js";
4
+ import { remoteComponentOutputPath } from "./demo-components.js";
5
+ import { appHasApi, appI18nNamespace, remoteDependencyAlias, shellApp } from "./descriptors.js";
6
+ import { packageName, tailwindPrefixForApp } from "./naming.js";
7
+ import { createCloudflareSecurityContract } from "./policy.js";
8
+ import { publicSurfaceManagedSourceAssetPaths } from "./public-surface.js";
9
+ import { createLocalisedUrlsMap, createRouteMetaFilePath, createRouteOwnedI18nPaths, createRoutePageFilePath } from "./routes.js";
10
+ import { CLOUDFLARE_COMPATIBILITY_DATE, EFFECT_VERSION, MODULE_FEDERATION_VERSION, NODE_VERSION, PNPM_VERSION } from "./versions.js";
11
+ import { createWorkspaceRootPackageScripts, createWorkspaceRootScriptPlan } from "./workspace-script-plan.js";
12
+ function createWorkspaceValidationContract(scope, enableTailwind, remotes = []) {
13
+ const fullStackVerticals = remotes.filter(appHasApi).map((remote)=>({
14
+ id: remote.id,
15
+ domain: remote.domain,
16
+ stem: remote.api.stem,
17
+ group: verticalApiGroupName(remote),
18
+ path: remote.directory,
19
+ port: remote.port,
20
+ mfName: remote.mfName,
21
+ apiPrefix: remote.api.prefix,
22
+ tailwindPrefix: tailwindPrefixForApp(remote),
23
+ zephyrAlias: remoteDependencyAlias(remote),
24
+ packageName: packageName(scope, remote.packageSuffix),
25
+ backendFederation: createBackendFederationMetadata(scope, remote),
26
+ deliveryUnit: createDeliveryUnitRecord(scope, remote),
27
+ exposes: Object.keys(remote.exposes ?? {}),
28
+ componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
29
+ namespace: appI18nNamespace(remote),
30
+ routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
31
+ routeMetaPaths: createRouteOwnedI18nPaths(remote).map((route)=>createRouteMetaFilePath(remote, route.canonicalPath)),
32
+ localisedUrls: createLocalisedUrlsMap(remote),
33
+ verticalRefs: remote.verticalRefs ?? []
34
+ }));
35
+ return {
36
+ packageScope: scope,
37
+ versions: {
38
+ cloudflareCompatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
39
+ effect: EFFECT_VERSION,
40
+ moduleFederation: MODULE_FEDERATION_VERSION,
41
+ node: NODE_VERSION,
42
+ pnpm: PNPM_VERSION
43
+ },
44
+ tailwindEnabled: enableTailwind,
45
+ fullStackVerticals,
46
+ shellNamespace: appI18nNamespace(shellApp),
47
+ oldRemotePaths: [
48
+ 'apps/remotes'
49
+ ],
50
+ scripts: createWorkspaceRootScriptPlan(remotes),
51
+ packageScripts: createWorkspaceRootPackageScripts(remotes),
52
+ cloudflareSecurity: createCloudflareSecurityContract(),
53
+ publicSurfaceManagedSourceAssetPaths: [
54
+ ...publicSurfaceManagedSourceAssetPaths
55
+ ],
56
+ shellRouteMetaPaths: createRouteOwnedI18nPaths(shellApp).map((route)=>createRouteMetaFilePath(shellApp, route.canonicalPath))
57
+ };
58
+ }
59
+ export { createWorkspaceValidationContract };
@@ -1,6 +1,7 @@
1
1
  import node_fs from "node:fs";
2
- import { createApiClient, createApiServiceEntry, createSharedApi, createShellApiClient } from "./api.js";
2
+ import { createApiClient, createApiServiceEntry, createBackendEffectApiExpose, createSharedApi, createShellApiClient } from "./api.js";
3
3
  import { createAppEnvDts, createAppRuntimeConfig, createAppStyles, createPostcssConfig, createSharedDesignTokensCss, createShellFrameComponent, createTailwindConfig } from "./app-files.js";
4
+ import { createBackendFederationContractFile } from "./backend-federation.js";
4
5
  import { normalizeUltramodernBridgeConfig } from "./bridge-config.js";
5
6
  import { createDevelopmentOverlay, createOwnership, createTopology, createUltramodernConfig } from "./contracts.js";
6
7
  import { createLayout, createRemoteEntry, createRemoteExposeComponent, createRemotePage, createShellPage, createShellRemoteComponents, remoteComponentOutputPath } from "./demo-components.js";
@@ -8,7 +9,7 @@ import { ULTRAMODERN_CONFIG_PATH, appHasApi, appI18nNamespace, createShellHost,
8
9
  import { copyRootTemplate, formatGeneratedWorkspaceFiles, writeFile, writeFileReplacing, writeJson } from "./fs-io.js";
9
10
  import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
10
11
  import { createAppPublicLocaleMessages } from "./locales.js";
11
- import { createAppModernConfig, createRemoteModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, createUltramodernBuildReexportModule } from "./module-federation.js";
12
+ import { createAppModernConfig, createBackendModuleFederationConfig, createRemoteModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, createUltramodernBuildReexportModule } from "./module-federation.js";
12
13
  import { assertUniqueTailwindPrefixes, toPackageScope } from "./naming.js";
13
14
  import { runCodeSmithOverlays } from "./overlays.js";
14
15
  import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig, createSharedContractsIndex, createSharedPackage, createSharedPackageTsConfig, createTsConfigBase } from "./package-json.js";
@@ -16,6 +17,7 @@ import { resolvePackageSource } from "./package-source.js";
16
17
  import { createPublicWebAppArtifacts } from "./public-surface.js";
17
18
  import { DRIZZLE_ORM_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, NODE_VERSION, PNPM_VERSION, TANSTACK_ROUTER_CORE_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_VERSION } from "./versions.js";
18
19
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
20
+ import { createZeropsYaml } from "./zerops.js";
19
21
  function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = [], bridge) {
20
22
  const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
21
23
  const publicWeb = createPublicWebAppArtifacts(resolvedApp);
@@ -31,7 +33,7 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
31
33
  writeFile(targetDir, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
32
34
  writeFile(targetDir, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
33
35
  writeFile(targetDir, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
34
- writeFile(targetDir, `${resolvedApp.directory}/modern.config.ts`, createAppModernConfig(scope, resolvedApp));
36
+ writeFile(targetDir, `${resolvedApp.directory}/modern.config.ts`, createAppModernConfig(scope, resolvedApp, remotes));
35
37
  writeFile(targetDir, `${resolvedApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(resolvedApp, scope, remotes));
36
38
  writeJson(targetDir, `${resolvedApp.directory}/locales/en/translation.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
37
39
  writeJson(targetDir, `${resolvedApp.directory}/locales/en/${appI18nNamespace(resolvedApp)}.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
@@ -43,6 +45,7 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
43
45
  writeFile(targetDir, `${resolvedApp.directory}/tailwind.config.ts`, createTailwindConfig());
44
46
  }
45
47
  writeFile(targetDir, `${resolvedApp.directory}/module-federation.config.ts`, 'shell' === resolvedApp.kind ? createShellModuleFederationConfig(scope, remotes) : createRemoteModuleFederationConfig(scope, resolvedApp, remotes));
48
+ if (appHasApi(resolvedApp)) writeFile(targetDir, `${resolvedApp.directory}/backend-federation.config.ts`, createBackendModuleFederationConfig(resolvedApp));
46
49
  writeAppFile('src/routes/layout.tsx', createLayout(resolvedApp.id));
47
50
  writeAppFile('src/routes/[lang]/page.tsx', 'shell' === resolvedApp.kind ? createShellPage(remotes) : createRemotePage(resolvedApp));
48
51
  for (const generatedFile of publicWeb.routeMetaFiles)writeFile(targetDir, generatedFile.path, generatedFile.content);
@@ -55,6 +58,8 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
55
58
  if (appHasApi(resolvedApp)) {
56
59
  writeFile(targetDir, `${resolvedApp.directory}/shared/api.ts`, createSharedApi(resolvedApp));
57
60
  writeFile(targetDir, `${resolvedApp.directory}/api/index.ts`, createApiServiceEntry(resolvedApp, '../shared/api.ts'));
61
+ writeFile(targetDir, `${resolvedApp.directory}/api/backend-federation.ts`, createBackendFederationContractFile(resolvedApp));
62
+ writeFile(targetDir, `${resolvedApp.directory}/api/effect-api.ts`, createBackendEffectApiExpose(scope, resolvedApp));
58
63
  writeFile(targetDir, `${resolvedApp.directory}/src/api/${resolvedApp.api.stem}-client.ts`, createApiClient(resolvedApp, '../../shared/api'));
59
64
  }
60
65
  if ('vertical' === resolvedApp.kind) {
@@ -104,7 +109,7 @@ function writePnpmWorkspacePackages(targetDir, bridge) {
104
109
  ...bridge.workspacePackages.map((entry)=>entry.pattern)
105
110
  ];
106
111
  const renderedPackages = packages.map((pattern)=>` - ${pattern}`).join('\n');
107
- writeFileReplacing(targetDir, 'pnpm-workspace.yaml', pnpmWorkspace.replace(/^packages:\n(?: {2}- .+\n)+/u, `packages:\n${renderedPackages}\n`));
112
+ writeFileReplacing(targetDir, 'pnpm-workspace.yaml', pnpmWorkspace.replace(/^packages:\r?\n(?: {2}- .+\r?\n)+/u, `packages:\n${renderedPackages}\n`));
108
113
  }
109
114
  function generateUltramodernWorkspace(options) {
110
115
  const beforeFiles = createFileSnapshot(options.targetDir);
@@ -141,11 +146,12 @@ function generateUltramodernWorkspace(options) {
141
146
  });
142
147
  writePnpmWorkspacePackages(options.targetDir, bridge);
143
148
  writeJson(options.targetDir, 'package.json', createCompactRootPackageJson(scope, packageSource, initialVerticals, bridge));
149
+ writeFile(options.targetDir, 'zerops.yaml', `${createZeropsYaml(scope, createdApps)}\n`);
144
150
  writeJson(options.targetDir, 'tsconfig.base.json', createTsConfigBase());
145
151
  writeJson(options.targetDir, 'tsconfig.json', createRootTsConfig(createdApps));
146
152
  writeJson(options.targetDir, 'topology/reference-topology.json', createTopology(scope, initialVerticals));
147
153
  writeJson(options.targetDir, 'topology/ownership.json', createOwnership(scope, initialVerticals));
148
- writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay(initialVerticals));
154
+ writeJson(options.targetDir, 'topology/local-overlays/development.json', createDevelopmentOverlay(scope, initialVerticals));
149
155
  writeJson(options.targetDir, ULTRAMODERN_CONFIG_PATH, createCompactUltramodernConfig(scope, options.modernVersion, packageSource, createdApps, enableTailwind, bridge));
150
156
  writeApp(options.targetDir, scope, shellApp, packageSource, enableTailwind, initialVerticals, bridge);
151
157
  for (const remote of initialVerticals)writeApp(options.targetDir, scope, remote, packageSource, enableTailwind, initialVerticals, bridge);
@@ -0,0 +1,62 @@
1
+ import { appHasApi, resolveApiPrefix, resolveApiStem } from "./descriptors.js";
2
+ import { packageName } from "./naming.js";
3
+ import { NODE_VERSION } from "./versions.js";
4
+ const zeropsNodeRuntime = `nodejs@${NODE_VERSION.split('.')[0]}`;
5
+ function quoteYamlString(value) {
6
+ return `'${value.replace(/'/gu, "''")}'`;
7
+ }
8
+ function readinessPath(app) {
9
+ if (!appHasApi(app)) return '/';
10
+ return `${resolveApiPrefix(app)}/${resolveApiStem(app)}/readiness`;
11
+ }
12
+ function createZeropsService(scope, app) {
13
+ const runtimeDirectory = `.zerops/runtime/${app.id}`;
14
+ const appPackageName = packageName(scope, app.packageSuffix);
15
+ const commands = [
16
+ 'curl https://mise.run | sh',
17
+ '~/.local/bin/mise install',
18
+ '~/.local/bin/mise exec -- pnpm install --frozen-lockfile',
19
+ `~/.local/bin/mise exec -- pnpm --filter ${appPackageName} run build`,
20
+ `~/.local/bin/mise exec -- pnpm run zerops:materialize -- --app ${app.id} --package ${appPackageName} --package-dir ${app.directory}`,
21
+ `cp topology/reference-topology.json ${runtimeDirectory}/topology.json`,
22
+ `cp topology/local-overlays/development.json ${runtimeDirectory}/local-overlay.json`
23
+ ];
24
+ return [
25
+ ` - setup: ${app.id}`,
26
+ ' build:',
27
+ ` base: ${zeropsNodeRuntime}`,
28
+ ' buildCommands:',
29
+ ' - |',
30
+ ...commands.map((command)=>` ${command}`),
31
+ ' deployFiles:',
32
+ ` - ${runtimeDirectory}`,
33
+ ' deploy:',
34
+ ' temporaryShutdown: false',
35
+ ' readinessCheck:',
36
+ ' httpGet:',
37
+ ` port: ${app.port}`,
38
+ ` path: ${readinessPath(app)}`,
39
+ ' failureTimeout: 120',
40
+ ' retryPeriod: 10',
41
+ ' run:',
42
+ ` base: ${zeropsNodeRuntime}`,
43
+ ' ports:',
44
+ ` - port: ${app.port}`,
45
+ ' protocol: TCP',
46
+ ' httpSupport: true',
47
+ ' envVariables:',
48
+ ' NODE_ENV: production',
49
+ ` ${app.portEnv}: ${quoteYamlString(String(app.port))}`,
50
+ ` ULTRAMODERN_ZEROPS_SERVICE: ${quoteYamlString(app.id)}`,
51
+ ` start: cd ${runtimeDirectory} && npm run serve`
52
+ ].join('\n');
53
+ }
54
+ function createZeropsYaml(scope, apps) {
55
+ return [
56
+ '# Generated by Modern.js UltraModern workspace tooling.',
57
+ '# Zerops runs each full-stack MicroVertical as a package-owned Node service.',
58
+ 'zerops:',
59
+ ...apps.map((app)=>createZeropsService(scope, app))
60
+ ].join('\n\n');
61
+ }
62
+ export { createZeropsYaml };