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