@bleedingdev/modern-js-create 3.5.0-ultramodern.3 → 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.
Files changed (93) hide show
  1. package/README.md +21 -17
  2. package/dist/cjs/ultramodern-tooling/commands.cjs +772 -75
  3. package/dist/cjs/ultramodern-tooling/config.cjs +149 -8
  4. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +25 -0
  5. package/dist/cjs/ultramodern-workspace/api.cjs +44 -0
  6. package/dist/cjs/ultramodern-workspace/app-files.cjs +8 -11
  7. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +282 -0
  8. package/dist/cjs/ultramodern-workspace/contracts.cjs +24 -8
  9. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +157 -0
  10. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +88 -0
  11. package/dist/cjs/ultramodern-workspace/descriptors.cjs +24 -0
  12. package/dist/cjs/ultramodern-workspace/module-federation.cjs +161 -50
  13. package/dist/cjs/ultramodern-workspace/package-json.cjs +26 -43
  14. package/dist/cjs/ultramodern-workspace/pnpm-workspace-policy-plan.cjs +166 -0
  15. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +153 -0
  16. package/dist/cjs/ultramodern-workspace/versions.cjs +8 -3
  17. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +175 -0
  18. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +104 -63
  19. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +97 -0
  20. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +11 -3
  21. package/dist/cjs/ultramodern-workspace/zerops.cjs +100 -0
  22. package/dist/esm/ultramodern-tooling/commands.js +775 -78
  23. package/dist/esm/ultramodern-tooling/config.js +144 -9
  24. package/dist/esm/ultramodern-workspace/add-vertical.js +26 -1
  25. package/dist/esm/ultramodern-workspace/api.js +43 -2
  26. package/dist/esm/ultramodern-workspace/app-files.js +8 -11
  27. package/dist/esm/ultramodern-workspace/backend-federation.js +217 -0
  28. package/dist/esm/ultramodern-workspace/contracts.js +24 -8
  29. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +108 -0
  30. package/dist/esm/ultramodern-workspace/delivery-unit.js +31 -0
  31. package/dist/esm/ultramodern-workspace/descriptors.js +13 -1
  32. package/dist/esm/ultramodern-workspace/module-federation.js +160 -42
  33. package/dist/esm/ultramodern-workspace/package-json.js +16 -40
  34. package/dist/esm/ultramodern-workspace/pnpm-workspace-policy-plan.js +113 -0
  35. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
  36. package/dist/esm/ultramodern-workspace/versions.js +6 -4
  37. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +110 -0
  38. package/dist/esm/ultramodern-workspace/workspace-scripts.js +88 -65
  39. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +59 -0
  40. package/dist/esm/ultramodern-workspace/write-workspace.js +14 -6
  41. package/dist/esm/ultramodern-workspace/zerops.js +62 -0
  42. package/dist/esm-node/ultramodern-tooling/commands.js +775 -78
  43. package/dist/esm-node/ultramodern-tooling/config.js +144 -9
  44. package/dist/esm-node/ultramodern-workspace/add-vertical.js +26 -1
  45. package/dist/esm-node/ultramodern-workspace/api.js +43 -2
  46. package/dist/esm-node/ultramodern-workspace/app-files.js +8 -11
  47. package/dist/esm-node/ultramodern-workspace/backend-federation.js +218 -0
  48. package/dist/esm-node/ultramodern-workspace/contracts.js +24 -8
  49. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +109 -0
  50. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +32 -0
  51. package/dist/esm-node/ultramodern-workspace/descriptors.js +13 -1
  52. package/dist/esm-node/ultramodern-workspace/module-federation.js +160 -42
  53. package/dist/esm-node/ultramodern-workspace/package-json.js +16 -40
  54. package/dist/esm-node/ultramodern-workspace/pnpm-workspace-policy-plan.js +114 -0
  55. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
  56. package/dist/esm-node/ultramodern-workspace/versions.js +6 -4
  57. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +111 -0
  58. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +88 -65
  59. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +60 -0
  60. package/dist/esm-node/ultramodern-workspace/write-workspace.js +14 -6
  61. package/dist/esm-node/ultramodern-workspace/zerops.js +63 -0
  62. package/dist/types/ultramodern-tooling/config.d.ts +6 -0
  63. package/dist/types/ultramodern-workspace/api.d.ts +1 -0
  64. package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
  65. package/dist/types/ultramodern-workspace/contracts.d.ts +1 -1
  66. package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
  67. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +27 -0
  68. package/dist/types/ultramodern-workspace/descriptors.d.ts +4 -0
  69. package/dist/types/ultramodern-workspace/module-federation.d.ts +4 -9
  70. package/dist/types/ultramodern-workspace/package-json.d.ts +0 -1
  71. package/dist/types/ultramodern-workspace/pnpm-workspace-policy-plan.d.ts +39 -0
  72. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
  73. package/dist/types/ultramodern-workspace/versions.d.ts +5 -3
  74. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +72 -0
  75. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +13 -0
  76. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +42 -0
  77. package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
  78. package/package.json +3 -3
  79. package/template-workspace/.gitignore.handlebars +4 -0
  80. package/template-workspace/README.md.handlebars +18 -15
  81. package/template-workspace/patches/@module-federation__bridge-react@2.6.0.patch +140 -0
  82. package/template-workspace/patches/@module-federation__modern-js-v3@2.6.0.patch +57 -0
  83. package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +452 -0
  84. package/template-workspace/patches/effect-schema-error-type-id.patch +18 -0
  85. package/template-workspace/pnpm-workspace.yaml.handlebars +8 -0
  86. package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
  87. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
  88. package/templates/workspace-scripts/generate-node-backend-federation.mjs +270 -0
  89. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +448 -0
  90. package/templates/workspace-scripts/proof-cloudflare-version.mjs +237 -9
  91. package/templates/workspace-scripts/proof-node-backend-federation.mjs +729 -0
  92. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
  93. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +434 -18
@@ -3,6 +3,9 @@ import node_path from "node:path";
3
3
  import { normalizeUltramodernBridgeConfig } from "../ultramodern-workspace/bridge-config.js";
4
4
  import { ULTRAMODERN_CONFIG_PATH, createNeutralOwnership, shellApp } from "../ultramodern-workspace/descriptors.js";
5
5
  import { toKebabCase } from "../ultramodern-workspace/naming.js";
6
+ const LEGACY_GENERATED_CONTRACT_PATH = '.modernjs/ultramodern-generated-contract.json';
7
+ const LEGACY_PACKAGE_SOURCE_METADATA_PATH = '.modernjs/ultramodern-package-source.json';
8
+ const LEGACY_DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
6
9
  function readJsonObject(filePath) {
7
10
  const value = JSON.parse(node_fs.readFileSync(filePath, 'utf-8'));
8
11
  if (null === value || 'object' != typeof value || Array.isArray(value)) throw new Error(`UltraModern config must contain a JSON object: ${filePath}`);
@@ -11,11 +14,39 @@ function readJsonObject(filePath) {
11
14
  function readOptionalJsonObject(filePath) {
12
15
  return node_fs.existsSync(filePath) ? readJsonObject(filePath) : {};
13
16
  }
14
- function packageScopeFromRoot(workspaceRoot1) {
15
- const rootPackage = readOptionalJsonObject(node_path.join(workspaceRoot1, 'package.json'));
16
- return 'string' == typeof rootPackage.name && rootPackage.name.length > 0 ? rootPackage.name : node_path.basename(workspaceRoot1);
17
+ function packageScopeFromRoot(workspaceRoot) {
18
+ const rootPackage = readOptionalJsonObject(node_path.join(workspaceRoot, 'package.json'));
19
+ return 'string' == typeof rootPackage.name && rootPackage.name.length > 0 ? rootPackage.name : node_path.basename(workspaceRoot);
17
20
  }
18
- function normalizeCompactConfig(sourcePath, config) {
21
+ function packageSourceFromMetadata(workspaceRoot) {
22
+ const metadataPath = node_path.join(workspaceRoot, LEGACY_PACKAGE_SOURCE_METADATA_PATH);
23
+ if (!node_fs.existsSync(metadataPath)) return;
24
+ const metadata = readJsonObject(metadataPath);
25
+ const aliases = metadata.modernPackages?.aliases ?? {};
26
+ const firstAliasEntry = Object.entries(aliases).find((entry)=>'string' == typeof entry[1]);
27
+ const firstPackage = firstAliasEntry?.[0];
28
+ const firstAlias = firstAliasEntry?.[1];
29
+ const aliasScope = firstAlias?.match(/^@([^/]+)\//u)?.[1];
30
+ const unscopedName = firstPackage?.split('/').at(-1) ?? '';
31
+ const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
32
+ const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
33
+ return {
34
+ strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
35
+ modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : 'workspace:*',
36
+ registry: 'string' == typeof metadata.modernPackages?.registry ? metadata.modernPackages.registry : void 0,
37
+ aliasScope,
38
+ aliasPackageNamePrefix
39
+ };
40
+ }
41
+ function readOverlayPorts(workspaceRoot) {
42
+ const overlayPath = node_path.join(workspaceRoot, LEGACY_DEVELOPMENT_OVERLAY_PATH);
43
+ if (!node_fs.existsSync(overlayPath)) return {};
44
+ const overlay = readJsonObject(overlayPath);
45
+ const ports = overlay.ports;
46
+ if (null === ports || 'object' != typeof ports || Array.isArray(ports)) return {};
47
+ return Object.fromEntries(Object.entries(ports).filter((entry)=>'number' == typeof entry[1]));
48
+ }
49
+ function normalizeCompactConfig(workspaceRoot, sourcePath, config) {
19
50
  const packageSource = config.packageSource && 'object' == typeof config.packageSource ? {
20
51
  strategy: 'install' === config.packageSource.strategy ? 'install' : 'workspace',
21
52
  modernPackageVersion: 'string' == typeof config.packageSource.modernPackageVersion ? config.packageSource.modernPackageVersion : 'workspace:*',
@@ -68,10 +99,114 @@ function normalizeCompactConfig(sourcePath, config) {
68
99
  }
69
100
  };
70
101
  }
71
- function readUltramodernConfig(workspaceRoot1 = process.cwd()) {
72
- const compactPath = node_path.join(workspaceRoot1, ULTRAMODERN_CONFIG_PATH);
73
- if (node_fs.existsSync(compactPath)) return normalizeCompactConfig(compactPath, readJsonObject(compactPath));
74
- throw new Error(`Missing UltraModern config. Expected ${ULTRAMODERN_CONFIG_PATH}.`);
102
+ function normalizeCompactUltramodernConfig(workspaceRoot, compact) {
103
+ return normalizeCompactConfig(workspaceRoot, node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH), compact);
104
+ }
105
+ function synthesizeCompactUltramodernConfig(workspaceRoot) {
106
+ const contractPath = node_path.join(workspaceRoot, LEGACY_GENERATED_CONTRACT_PATH);
107
+ if (!node_fs.existsSync(contractPath)) return;
108
+ const contract = readJsonObject(contractPath);
109
+ const sources = [
110
+ LEGACY_GENERATED_CONTRACT_PATH
111
+ ];
112
+ const missing = [];
113
+ const packageSource = packageSourceFromMetadata(workspaceRoot);
114
+ if (packageSource) sources.push(LEGACY_PACKAGE_SOURCE_METADATA_PATH);
115
+ else missing.push(LEGACY_PACKAGE_SOURCE_METADATA_PATH);
116
+ const ports = readOverlayPorts(workspaceRoot);
117
+ if (node_fs.existsSync(node_path.join(workspaceRoot, LEGACY_DEVELOPMENT_OVERLAY_PATH))) sources.push(LEGACY_DEVELOPMENT_OVERLAY_PATH);
118
+ else missing.push(LEGACY_DEVELOPMENT_OVERLAY_PATH);
119
+ const apps = Array.isArray(contract.apps) ? contract.apps : [];
120
+ const shell = apps.find((app)=>app?.id === shellApp.id);
121
+ const compact = {
122
+ schemaVersion: 'number' == typeof contract.schemaVersion ? contract.schemaVersion : 1,
123
+ ...'string' == typeof contract.profile ? {
124
+ profile: contract.profile
125
+ } : {},
126
+ workspace: {
127
+ packageScope: packageScopeFromRoot(workspaceRoot)
128
+ },
129
+ ...packageSource ? {
130
+ packageSource: {
131
+ strategy: packageSource.strategy,
132
+ modernPackageVersion: packageSource.modernPackageVersion,
133
+ ...packageSource.registry ? {
134
+ registry: packageSource.registry
135
+ } : {},
136
+ ...packageSource.aliasScope ? {
137
+ aliasScope: packageSource.aliasScope
138
+ } : {},
139
+ ...packageSource.aliasPackageNamePrefix ? {
140
+ aliasPackageNamePrefix: packageSource.aliasPackageNamePrefix
141
+ } : {}
142
+ }
143
+ } : {},
144
+ features: {
145
+ tailwind: shell?.styling?.tailwind !== false
146
+ },
147
+ topology: {
148
+ apps: apps.map((app)=>{
149
+ const id = String(app.id);
150
+ const appPath = 'string' == typeof app.path ? app.path : id === shellApp.id ? shellApp.directory : `verticals/${toKebabCase(id)}`;
151
+ const domain = 'string' == typeof app.i18n?.namespace && 'shell' !== app.i18n.namespace ? app.i18n.namespace : appPath.split('/').at(-1);
152
+ const moduleFederation = app.moduleFederation && 'object' == typeof app.moduleFederation ? {
153
+ role: 'vertical' === app.kind ? 'remote' : 'host',
154
+ ...'string' == typeof app.moduleFederation.name ? {
155
+ name: app.moduleFederation.name
156
+ } : {},
157
+ ...Array.isArray(app.moduleFederation.exposes) ? {
158
+ exposes: app.moduleFederation.exposes.filter((expose)=>'string' == typeof expose)
159
+ } : {},
160
+ ...Array.isArray(app.moduleFederation.verticalRefs) ? {
161
+ verticalRefs: app.moduleFederation.verticalRefs.filter((ref)=>'string' == typeof ref)
162
+ } : {}
163
+ } : void 0;
164
+ const api = app.effect && 'object' == typeof app.effect ? {
165
+ stem: 'string' == typeof app.effect.prefix ? app.effect.prefix.split('/').filter(Boolean).at(-1) ?? domain ?? id : domain ?? id,
166
+ prefix: 'string' == typeof app.effect.prefix ? app.effect.prefix : `/${domain ?? id}-api`,
167
+ consumedBy: [
168
+ shellApp.id,
169
+ id
170
+ ]
171
+ } : void 0;
172
+ return {
173
+ id,
174
+ kind: 'vertical' === app.kind ? 'vertical' : 'shell',
175
+ path: appPath,
176
+ ...'string' == typeof app.package ? {
177
+ package: app.package
178
+ } : {},
179
+ packageSuffix: 'string' == typeof app.package ? app.package.split('/').at(-1) : appPath.split('/').at(-1),
180
+ ...id === shellApp.id ? {
181
+ displayName: shellApp.displayName
182
+ } : {},
183
+ ...domain ? {
184
+ domain
185
+ } : {},
186
+ ...'number' == typeof ports[id] ? {
187
+ port: ports[id]
188
+ } : {},
189
+ ...moduleFederation ? {
190
+ moduleFederation
191
+ } : {},
192
+ ...api ? {
193
+ api
194
+ } : {}
195
+ };
196
+ })
197
+ }
198
+ };
199
+ return {
200
+ compact,
201
+ missing,
202
+ sources
203
+ };
204
+ }
205
+ function readUltramodernConfig(workspaceRoot = process.cwd()) {
206
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
207
+ if (node_fs.existsSync(compactPath)) return normalizeCompactConfig(workspaceRoot, compactPath, readJsonObject(compactPath));
208
+ if (node_fs.existsSync(node_path.join(workspaceRoot, LEGACY_GENERATED_CONTRACT_PATH))) throw new Error(`Missing ${ULTRAMODERN_CONFIG_PATH}. Legacy UltraModern metadata detected — run \`modern-js-create ultramodern migrate-strict-effect\` to synthesize it.`);
209
+ throw new Error(`Missing UltraModern config. Expected ${ULTRAMODERN_CONFIG_PATH}. Run \`modern-js-create ultramodern migrate-strict-effect\` if you have legacy UltraModern metadata to migrate.`);
75
210
  }
76
211
  function workspaceAppsFromToolingConfig(config) {
77
212
  return config.topology.apps.map((app)=>{
@@ -117,4 +252,4 @@ function workspaceAppsFromToolingConfig(config) {
117
252
  };
118
253
  });
119
254
  }
120
- export { readUltramodernConfig, workspaceAppsFromToolingConfig };
255
+ export { normalizeCompactUltramodernConfig, readUltramodernConfig, synthesizeCompactUltramodernConfig, workspaceAppsFromToolingConfig };
@@ -5,12 +5,14 @@ import { WORKSPACE_PACKAGE_VERSION } from "../ultramodern-package-source.js";
5
5
  import { readUltramodernConfig } from "../ultramodern-tooling/config.js";
6
6
  import { apiTopologyMetadata, createShellApiClient } from "./api.js";
7
7
  import { createAppEnvDts, createAppRuntimeConfig, createShellFrameComponent } from "./app-files.js";
8
+ import { createBackendFederationContract, createServerExecutionOverlay } from "./backend-federation.js";
9
+ import { createDeliveryUnitRecord, deliveryUnitContractBlock } from "./delivery-unit.js";
8
10
  import { createShellPage, createShellRemoteComponents } from "./demo-components.js";
9
11
  import { ULTRAMODERN_CONFIG_PATH, appHasApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, remoteDependencyAlias, resolveApiPrefix, shellApp, zephyrRemoteDependency } from "./descriptors.js";
10
12
  import { formatGeneratedWorkspaceFiles, readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
11
13
  import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
12
14
  import { createAppPublicLocaleMessages } from "./locales.js";
13
- import { createShellModuleFederationConfig } from "./module-federation.js";
15
+ import { createAppModernConfig, createShellModuleFederationConfig } from "./module-federation.js";
14
16
  import { assertUniqueTailwindPrefixes, normalizePath, packageName, toEnvSegment, toKebabCase, toPackageScope, toPascalCase } from "./naming.js";
15
17
  import { runCodeSmithOverlays } from "./overlays.js";
16
18
  import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig } from "./package-json.js";
@@ -20,6 +22,7 @@ import { createPublicWebAppArtifacts, rewriteWorkspaceAssetsForApp } from "./pub
20
22
  import { isRecord } from "./types.js";
21
23
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
22
24
  import { createCompactUltramodernConfig, writeApp } from "./write-workspace.js";
25
+ import { createZeropsYaml } from "./zerops.js";
23
26
  const FIRST_VERTICAL_PORT = 4101;
24
27
  const TOPOLOGY_PATH = 'topology/reference-topology.json';
25
28
  const OWNERSHIP_PATH = 'topology/ownership.json';
@@ -73,6 +76,7 @@ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwin
73
76
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.json`), createAppTsConfig(shellHost, remotes));
74
77
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.mf-types.json`), createAppMfTypesTsConfig(shellHost));
75
78
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
79
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/modern.config.ts`, createAppModernConfig(scope, shellHost, remotes));
76
80
  writeFileReplacing(workspaceRoot, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
77
81
  writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
78
82
  writeFileReplacing(workspaceRoot, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
@@ -124,6 +128,12 @@ function verticalTopologyEntry(scope, vertical, remotes = []) {
124
128
  fallbackTelemetryEvent: 'modernjs:mv-runtime-parity',
125
129
  sharedContractVersion: 'mf-ssr-contract-v1'
126
130
  },
131
+ ...createBackendFederationContract(scope, vertical) ? {
132
+ backendFederation: createBackendFederationContract(scope, vertical)
133
+ } : {},
134
+ ...vertical.api ? {
135
+ deliveryUnit: deliveryUnitContractBlock(createDeliveryUnitRecord(scope, vertical))
136
+ } : {},
127
137
  ...apiTopologyMetadata(vertical) ? {
128
138
  api: apiTopologyMetadata(vertical)
129
139
  } : {},
@@ -364,6 +374,12 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
364
374
  pointer: `/apis/${vertical.id}`,
365
375
  description: `Add local API URL for ${vertical.id}`,
366
376
  value: `http://localhost:${vertical.port}${resolveApiPrefix(vertical)}`
377
+ },
378
+ {
379
+ path: DEVELOPMENT_OVERLAY_PATH,
380
+ pointer: `/serverExecution/${vertical.id}`,
381
+ description: `Add local MicroVertical server execution metadata for ${vertical.id}`,
382
+ value: createServerExecutionOverlay(scope, vertical)
367
383
  }
368
384
  ] : [];
369
385
  return [
@@ -479,6 +495,8 @@ function addUltramodernVertical(options) {
479
495
  overlay.ports[vertical.id] = vertical.port;
480
496
  overlay.manifests ??= {};
481
497
  overlay.manifests[vertical.id] = `http://localhost:${vertical.port}/mf-manifest.json`;
498
+ overlay.serverExecution ??= {};
499
+ overlay.serverExecution[vertical.id] = createServerExecutionOverlay(scope, vertical);
482
500
  overlay.apis ??= {};
483
501
  overlay.apis[vertical.id] = `http://localhost:${vertical.port}${resolveApiPrefix(vertical)}`;
484
502
  writeJsonFile(topologyPath, topology);
@@ -491,6 +509,13 @@ function addUltramodernVertical(options) {
491
509
  },
492
510
  ...updatedVerticals
493
511
  ], enableTailwind, bridge));
512
+ writeFileReplacing(options.workspaceRoot, 'zerops.yaml', `${createZeropsYaml(scope, [
513
+ {
514
+ ...shellApp,
515
+ verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
516
+ },
517
+ ...updatedVerticals
518
+ ])}\n`);
494
519
  rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals, bridge);
495
520
  writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
496
521
  addShellZephyrDependency(options.workspaceRoot, scope, vertical);
@@ -1,4 +1,5 @@
1
- import { appHasApi, resolveApiPrefix, resolveApiStem, verticalApiApps } from "./descriptors.js";
1
+ import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from "./backend-federation.js";
2
+ import { appHasApi, createBackendFederationName, resolveApiPrefix, resolveApiStem, verticalApiApps } from "./descriptors.js";
2
3
  import { packageName, toCamelCase, toPascalCase } from "./naming.js";
3
4
  function verticalApiExport(service) {
4
5
  return `${toCamelCase(resolveApiStem(service))}Api`;
@@ -639,6 +640,46 @@ const apiRuntime: EffectBffDefinition<typeof ${apiExport}, EffectRuntimeLayer> &
639
640
  layer,
640
641
  });
641
642
 
643
+ export default apiRuntime;
644
+ `;
645
+ }
646
+ function createBackendEffectApiExpose(scope, service) {
647
+ const apiExport = verticalApiExport(service);
648
+ const groupName = verticalApiGroupName(service);
649
+ const apiPrefix = resolveApiPrefix(service);
650
+ const stem = resolveApiStem(service);
651
+ return `import apiRuntime from './index.ts';
652
+ import { ultramodernApiMarker } from '../shared/ultramodern-build.ts';
653
+ import {
654
+ ${apiExport},
655
+ ${groupName}ApiContract,
656
+ ${groupName}OperationContexts,
657
+ } from '../shared/api.ts';
658
+
659
+ export const backendFederationContract = {
660
+ compatibility: {
661
+ build: ultramodernApiMarker.build,
662
+ contractVersion: '${BACKEND_FEDERATION_CONTRACT_VERSION}',
663
+ nodeAdapterVersion: '${BACKEND_FEDERATION_NODE_ADAPTER_VERSION}',
664
+ packageName: '${packageName(scope, service.packageSuffix)}',
665
+ sourceRevision: ultramodernApiMarker.sourceRevision,
666
+ unitId: ultramodernApiMarker.unitId,
667
+ },
668
+ executionSurfaces: ['node-mf-runtime'],
669
+ exposes: ['./effect-api'],
670
+ name: '${createBackendFederationName(service)}',
671
+ openapiPath: '${apiPrefix}/openapi.json',
672
+ readinessPath: '${apiPrefix}/${stem}/readiness',
673
+ role: 'microvertical-server',
674
+ runtimeFramework: 'effect',
675
+ strictEffectApproach: true,
676
+ } as const;
677
+
678
+ export const api: unknown = ${apiExport};
679
+ export const contract = ${groupName}ApiContract;
680
+ export const operationContexts = ${groupName}OperationContexts;
681
+ export const runtime = apiRuntime;
682
+
642
683
  export default apiRuntime;
643
684
  `;
644
685
  }
@@ -981,4 +1022,4 @@ function createApiOperationContract(target) {
981
1022
  }
982
1023
  };
983
1024
  }
984
- export { apiTopologyMetadata, createApiClient, createApiDomainOperations, createApiOperationContract, createApiReadinessContract, createApiRequestContextContract, createApiServiceEntry, createSharedApi, createSharedApiContract, createSharedApiImports, createShellApiClient, verticalApiCreatePayloadSchemaExport, verticalApiErrorStem, verticalApiExport, verticalApiGroupName, verticalApiMarkerSchemaExport, verticalApiName, verticalApiNotFoundErrorExport, verticalApiNotFoundSchemaExport, verticalApiReadinessSchemaExport, verticalApiSchemaExport };
1025
+ export { apiTopologyMetadata, createApiClient, createApiDomainOperations, createApiOperationContract, createApiReadinessContract, createApiRequestContextContract, createApiServiceEntry, createBackendEffectApiExpose, createSharedApi, createSharedApiContract, createSharedApiImports, createShellApiClient, verticalApiCreatePayloadSchemaExport, verticalApiErrorStem, verticalApiExport, verticalApiGroupName, verticalApiMarkerSchemaExport, verticalApiName, verticalApiNotFoundErrorExport, verticalApiNotFoundSchemaExport, verticalApiReadinessSchemaExport, verticalApiSchemaExport };
@@ -27,17 +27,14 @@ function createAppEnvDts(app, remotes = []) {
27
27
  }
28
28
  `;
29
29
  })).join('\n');
30
- const reactTypeReference = remoteModuleDeclarations ? "/// <reference types='react' />\n" : '';
31
- const siteUrlDeclaration = 'declare const ULTRAMODERN_SITE_URL: string;';
32
- return `${reactTypeReference}/// <reference types='@modern-js/app-tools/types' />
33
-
34
- ${siteUrlDeclaration}
35
- declare module '*.svg' {
36
- const url: string;
37
- export default url;
38
- }
39
- declare module '*.css';
40
- ${remoteModuleDeclarations ? `\n${remoteModuleDeclarations}` : ''}`;
30
+ const reactTypeImport = remoteModuleDeclarations ? "import type React from 'react';\n" : '';
31
+ return [
32
+ `import '@modern-js/app-tools/types';\n${reactTypeImport}`.trimEnd(),
33
+ `declare global {
34
+ const ULTRAMODERN_SITE_URL: string;
35
+ }`,
36
+ remoteModuleDeclarations.trimEnd()
37
+ ].filter((section)=>section.length > 0).join('\n\n').concat('\n');
41
38
  }
42
39
  function createAppRuntimeConfig(app, scope, remotes = []) {
43
40
  const pluginsConfig = 'shell' === app.kind ? ` plugins: [
@@ -0,0 +1,217 @@
1
+ import { verticalApiExport, verticalApiGroupName } from "./api.js";
2
+ import { createDeliveryUnitRecord, deliveryUnitContractBlock } from "./delivery-unit.js";
3
+ import { appHasApi, createBackendFederationContainerEntry, createBackendFederationManifestEnv, createBackendFederationManifestUrl, createBackendFederationName, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createRemoteManifestEnv, resolveApiPrefix, resolveApiStem } from "./descriptors.js";
4
+ import { packageName, toEnvSegment } from "./naming.js";
5
+ import { EFFECT_VERSION, MODULE_FEDERATION_VERSION } from "./versions.js";
6
+ const BACKEND_FEDERATION_CONTRACT_VERSION = 'microvertical-server-effect-v1';
7
+ const BACKEND_FEDERATION_NODE_ADAPTER_VERSION = 'backend-mf-effect-v1';
8
+ function createZephyrEnv(app, suffix) {
9
+ return `ZEPHYR_${toEnvSegment(app.domain ?? app.id)}_${suffix}`;
10
+ }
11
+ function createWorkerBindingName(app) {
12
+ return `VERTICAL_${toEnvSegment(app.domain ?? app.id)}_WORKER`;
13
+ }
14
+ function createWorkerBindingEnv(app) {
15
+ return `VERTICAL_${toEnvSegment(app.domain ?? app.id)}_WORKER_BINDING`;
16
+ }
17
+ function createDispatchNamespaceEnv(app) {
18
+ return `VERTICAL_${toEnvSegment(app.domain ?? app.id)}_DISPATCH_NAMESPACE`;
19
+ }
20
+ function createDispatchWorkerNameEnv(app) {
21
+ return `VERTICAL_${toEnvSegment(app.domain ?? app.id)}_WORKER_NAME`;
22
+ }
23
+ function createEffectExpose(app) {
24
+ const apiStem = resolveApiStem(app);
25
+ return {
26
+ contract: `${app.directory}/shared/api.ts`,
27
+ runtime: `${app.directory}/api/index.ts`,
28
+ client: `${app.directory}/src/api/${app.api.stem}-client.ts`,
29
+ openapi: `${app.api.prefix}/openapi.json`,
30
+ readiness: `${app.api.prefix}/${apiStem}/readiness`
31
+ };
32
+ }
33
+ function createCloudflareExecutionSurface(scope, app) {
34
+ return {
35
+ kind: 'cloudflare-worker-snapshot',
36
+ workerName: createCloudflareWorkerName(scope, app),
37
+ publicUrlEnv: createCloudflarePublicUrlEnv(app),
38
+ ssr: {
39
+ workerEntry: '.output/server/index.mjs',
40
+ workerManifest: '.output/server/modern-worker-manifest.json',
41
+ routeManifest: '.output/server/route.json',
42
+ ssrBundle: '.output/worker/index.js',
43
+ effectBffBundle: '.output/worker/__modern_bff_effect.js',
44
+ assetsBinding: 'ASSETS'
45
+ },
46
+ zephyr: {
47
+ runtime: 'ssr-worker',
48
+ integration: 'managed-cloudflare',
49
+ snapshotIdEnv: createZephyrEnv(app, 'SNAPSHOT_ID'),
50
+ versionIdEnv: createZephyrEnv(app, 'VERSION_ID'),
51
+ applicationUidEnv: createZephyrEnv(app, 'APPLICATION_UID')
52
+ },
53
+ workerDispatch: {
54
+ preferred: 'service-binding',
55
+ serviceBinding: createWorkerBindingName(app),
56
+ serviceBindingEnv: createWorkerBindingEnv(app),
57
+ dispatchNamespaceEnv: createDispatchNamespaceEnv(app),
58
+ dispatchWorkerNameEnv: createDispatchWorkerNameEnv(app),
59
+ requestInterface: 'fetch'
60
+ }
61
+ };
62
+ }
63
+ function createNodeExecutionSurface(app) {
64
+ return {
65
+ kind: 'node-mf-runtime',
66
+ adapterVersion: BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
67
+ remoteName: createBackendFederationName(app),
68
+ manifestEnv: createBackendFederationManifestEnv(app),
69
+ manifestUrl: createBackendFederationManifestUrl(app),
70
+ containerEntry: createBackendFederationContainerEntry(app),
71
+ remoteType: 'module',
72
+ expose: './effect-api',
73
+ runtimePackage: '@modern-js/plugin-bff/effect'
74
+ };
75
+ }
76
+ function createServerExecutionOverlay(scope, app) {
77
+ if (!appHasApi(app)) return;
78
+ const deliveryUnit = createDeliveryUnitRecord(scope, app);
79
+ return {
80
+ apiBaseUrl: `http://localhost:${app.port}${resolveApiPrefix(app)}`,
81
+ versionBoundary: 'web-and-api-same-build',
82
+ deliveryUnit: {
83
+ unitId: deliveryUnit.unitId,
84
+ buildMarker: deliveryUnit.buildMarker
85
+ },
86
+ cloudflare: createCloudflareExecutionSurface(scope, app),
87
+ node: createNodeExecutionSurface(app)
88
+ };
89
+ }
90
+ function createBackendFederationContract(scope, app) {
91
+ if (!appHasApi(app)) return;
92
+ const readiness = `${app.api.prefix}/${resolveApiStem(app)}/readiness`;
93
+ const deliveryUnit = createDeliveryUnitRecord(scope, app);
94
+ return {
95
+ role: 'microvertical-server',
96
+ name: createBackendFederationName(app),
97
+ runtimeFramework: 'effect',
98
+ strictEffectApproach: true,
99
+ deliveryUnit: deliveryUnitContractBlock(deliveryUnit),
100
+ exposes: {
101
+ './effect-api': createEffectExpose(app)
102
+ },
103
+ versionBoundary: {
104
+ invariant: 'web-and-api-same-build',
105
+ identityRoot: 'deliveryUnit',
106
+ packageName: packageName(scope, app.packageSuffix),
107
+ ui: {
108
+ manifestEnv: createRemoteManifestEnv(app),
109
+ manifestUrl: `http://localhost:${app.port}/mf-manifest.json`,
110
+ buildMarker: `${app.directory}/src/routes/ultramodern-route-metadata.ts`
111
+ },
112
+ api: {
113
+ readiness,
114
+ buildMarker: `${app.directory}/shared/ultramodern-build.ts`,
115
+ publicUrlEnv: createCloudflarePublicUrlEnv(app)
116
+ }
117
+ },
118
+ executionSurfaces: {
119
+ cloudflare: createCloudflareExecutionSurface(scope, app),
120
+ node: createNodeExecutionSurface(app)
121
+ },
122
+ compatibility: {
123
+ contractVersion: BACKEND_FEDERATION_CONTRACT_VERSION,
124
+ packageName: packageName(scope, app.packageSuffix),
125
+ effectVersion: EFFECT_VERSION,
126
+ moduleFederationVersion: MODULE_FEDERATION_VERSION
127
+ },
128
+ cache: {
129
+ cloudflareSnapshot: 'immutable',
130
+ nodeManifest: 'no-store',
131
+ nodeVersionedContainer: 'immutable',
132
+ nodeUnpinnedContainer: 'revalidate'
133
+ },
134
+ fallback: {
135
+ timeoutMs: 1500,
136
+ failureEvent: 'modernjs:microvertical-server-fallback',
137
+ strategy: 'typed-effect-error'
138
+ }
139
+ };
140
+ }
141
+ function createBackendFederationMetadata(scope, app) {
142
+ if (!appHasApi(app)) return;
143
+ return {
144
+ contractVersion: BACKEND_FEDERATION_CONTRACT_VERSION,
145
+ deliveryUnit: deliveryUnitContractBlock(createDeliveryUnitRecord(scope, app)),
146
+ executionSurfaces: [
147
+ 'node-mf-runtime'
148
+ ],
149
+ exposes: [
150
+ './effect-api'
151
+ ],
152
+ name: createBackendFederationName(app),
153
+ nodeAdapterVersion: BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
154
+ openapiPath: `${app.api.prefix}/openapi.json`,
155
+ readinessPath: `${app.api.prefix}/${resolveApiStem(app)}/readiness`,
156
+ role: 'microvertical-server',
157
+ runtimeFramework: 'effect',
158
+ strictEffectApproach: true
159
+ };
160
+ }
161
+ function createBackendFederationContractFile(app) {
162
+ if (!app.api) throw new Error(`App ${app.id} does not define an Effect API.`);
163
+ const apiExport = verticalApiExport(app);
164
+ const groupName = verticalApiGroupName(app);
165
+ const readinessPath = `${app.api.prefix}/${resolveApiStem(app)}/readiness`;
166
+ return `import runtime from './index.ts';
167
+ import { ultramodernApiMarker } from '../shared/ultramodern-build.ts';
168
+ import {
169
+ ${apiExport} as api,
170
+ ${groupName}ApiContract as contract,
171
+ ${groupName}OperationContexts as operationContexts,
172
+ } from '../shared/api.ts';
173
+
174
+ export const backendFederationContract = {
175
+ compatibility: {
176
+ build: ultramodernApiMarker.build,
177
+ contractVersion: '${BACKEND_FEDERATION_CONTRACT_VERSION}',
178
+ nodeAdapterVersion: '${BACKEND_FEDERATION_NODE_ADAPTER_VERSION}',
179
+ packageName: ultramodernApiMarker.packageName,
180
+ sourceRevision: ultramodernApiMarker.sourceRevision,
181
+ unitId: ultramodernApiMarker.unitId,
182
+ },
183
+ contractVersion: '${BACKEND_FEDERATION_CONTRACT_VERSION}',
184
+ executionSurfaces: ['node-mf-runtime'],
185
+ exposes: ['./effect-api'],
186
+ name: '${createBackendFederationName(app)}',
187
+ nodeAdapterVersion: '${BACKEND_FEDERATION_NODE_ADAPTER_VERSION}',
188
+ openapiPath: '${app.api.prefix}/openapi.json',
189
+ readinessPath: '${readinessPath}',
190
+ role: 'microvertical-server',
191
+ runtimeFramework: 'effect',
192
+ strictEffectApproach: true,
193
+ } as const;
194
+
195
+ export { api, contract, operationContexts, runtime };
196
+
197
+ export default runtime;
198
+ `;
199
+ }
200
+ function createBackendFederationSummary(scope, app) {
201
+ if (!appHasApi(app)) return;
202
+ return {
203
+ id: app.id,
204
+ path: app.directory,
205
+ role: 'microvertical-server',
206
+ name: createBackendFederationName(app),
207
+ runtimeFramework: 'effect',
208
+ strictEffectApproach: true,
209
+ contractVersion: BACKEND_FEDERATION_CONTRACT_VERSION,
210
+ deliveryUnit: deliveryUnitContractBlock(createDeliveryUnitRecord(scope, app)),
211
+ executionSurfaces: {
212
+ cloudflare: createCloudflareExecutionSurface(scope, app),
213
+ node: createNodeExecutionSurface(app)
214
+ }
215
+ };
216
+ }
217
+ export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, createBackendFederationContract, createBackendFederationContractFile, createBackendFederationMetadata, createBackendFederationSummary, createDispatchWorkerNameEnv, createServerExecutionOverlay, createWorkerBindingEnv, createWorkerBindingName };
@@ -1,7 +1,10 @@
1
1
  import { apiTopologyMetadata } from "./api.js";
2
+ import { createBackendFederationContract, createBackendFederationSummary, createServerExecutionOverlay } from "./backend-federation.js";
3
+ import { createDeliveryUnitRecord, deliveryUnitContractBlock } from "./delivery-unit.js";
2
4
  import { createModuleFederationRemoteContracts, createShellHost, resolveApiPrefix, sharedPackages, shellApp, verticalApiApps } from "./descriptors.js";
3
5
  import { packageName } from "./naming.js";
4
6
  import { createCloudflareDeployContract } from "./policy.js";
7
+ import { createGeneratedToolingWrapperMap } from "./tooling-command-catalog.js";
5
8
  import { CLOUDFLARE_COMPATIBILITY_DATE, NODE_VERSION, PNPM_VERSION } from "./versions.js";
6
9
  function createTopology(scope, remotes = []) {
7
10
  const shellHost = createShellHost(remotes);
@@ -44,6 +47,12 @@ function createTopology(scope, remotes = []) {
44
47
  fallbackTelemetryEvent: 'modernjs:mv-runtime-parity',
45
48
  sharedContractVersion: 'mf-ssr-contract-v1'
46
49
  },
50
+ ...createBackendFederationContract(scope, vertical) ? {
51
+ backendFederation: createBackendFederationContract(scope, vertical)
52
+ } : {},
53
+ ...vertical.api ? {
54
+ deliveryUnit: deliveryUnitContractBlock(createDeliveryUnitRecord(scope, vertical))
55
+ } : {},
47
56
  ...apiTopologyMetadata(vertical) ? {
48
57
  api: apiTopologyMetadata(vertical)
49
58
  } : {},
@@ -99,7 +108,7 @@ function createOwnership(scope, remotes = []) {
99
108
  }))
100
109
  };
101
110
  }
102
- function createDevelopmentOverlay(remotes = []) {
111
+ function createDevelopmentOverlay(scope, remotes = []) {
103
112
  return {
104
113
  schemaVersion: 1,
105
114
  environment: 'development',
@@ -115,6 +124,10 @@ function createDevelopmentOverlay(remotes = []) {
115
124
  remote.id,
116
125
  `http://localhost:${remote.port}/mf-manifest.json`
117
126
  ])),
127
+ serverExecution: Object.fromEntries(verticalApiApps(remotes).map((app)=>[
128
+ app.id,
129
+ createServerExecutionOverlay(scope, app)
130
+ ])),
118
131
  apis: Object.fromEntries(verticalApiApps(remotes).map((app)=>[
119
132
  app.id,
120
133
  `http://localhost:${app.port}${resolveApiPrefix(app)}`
@@ -191,6 +204,12 @@ function createUltramodernConfig(scope, modernVersion, packageSource, apps = [
191
204
  tsConfigPath: './tsconfig.mf-types.json'
192
205
  }
193
206
  },
207
+ ...createBackendFederationContract(scope, app) ? {
208
+ backendFederation: createBackendFederationContract(scope, app)
209
+ } : {},
210
+ ...app.api ? {
211
+ deliveryUnit: deliveryUnitContractBlock(createDeliveryUnitRecord(scope, app))
212
+ } : {},
194
213
  ...app.api ? {
195
214
  api: {
196
215
  stem: app.api.stem,
@@ -234,6 +253,9 @@ function createUltramodernConfig(scope, modernVersion, packageSource, apps = [
234
253
  hostOnly: 'shell' === app.kind && 0 === Object.keys(app.exposes ?? {}).length
235
254
  }))
236
255
  },
256
+ backendFederation: {
257
+ apps: verticalApiApps(remotes).map((app)=>createBackendFederationSummary(scope, app))
258
+ },
237
259
  agentSkills: {
238
260
  target: 'codex',
239
261
  lockfile: './.codex/skills-lock.json',
@@ -248,13 +270,7 @@ function createUltramodernConfig(scope, modernVersion, packageSource, apps = [
248
270
  tooling: {
249
271
  command: 'modern-js-create ultramodern',
250
272
  wrappers: {
251
- validate: "scripts/validate-ultramodern-workspace.mts",
252
- typecheck: "scripts/ultramodern-typecheck.mts",
253
- mfTypes: "scripts/assert-mf-types.mts",
254
- publicSurface: "scripts/generate-public-surface-assets.mts",
255
- cloudflareProof: "scripts/proof-cloudflare-version.mts",
256
- performanceReadiness: "scripts/ultramodern-performance-readiness.mts",
257
- migrateStrictEffect: "scripts/migrate-strict-effect.mts",
273
+ ...createGeneratedToolingWrapperMap(),
258
274
  apiBoundaries: "scripts/check-ultramodern-api-boundaries.mts",
259
275
  skills: "scripts/bootstrap-agent-skills.mts"
260
276
  }