@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
@@ -0,0 +1,18 @@
1
+ diff --git a/dist/internal/schema/schema.d.ts b/dist/internal/schema/schema.d.ts
2
+ index 7fbeec1a56a85ab1569f8b80e319fb4638a91615..21d38f72238f748d989d3080f77d0edfc63c0e16 100644
3
+ --- a/dist/internal/schema/schema.d.ts
4
+ +++ b/dist/internal/schema/schema.d.ts
5
+ @@ -3,0 +4 @@
6
+ +export declare const SchemaErrorTypeId: "~effect/Schema/SchemaError";
7
+ diff --git a/dist/unstable/http/internal/preResponseHandler.d.ts b/dist/unstable/http/internal/preResponseHandler.d.ts
8
+ index 81e81e744fa55db2647a8483991db403d451e7dd..040d6c97b85d0c01d4a477013010b9f54d462de8 100644
9
+ --- a/dist/unstable/http/internal/preResponseHandler.d.ts
10
+ +++ b/dist/unstable/http/internal/preResponseHandler.d.ts
11
+ @@ -1 +1,6 @@
12
+ -export {};
13
+ +import type * as Effect from "../../../Effect.ts";
14
+ +import type { HttpServerError } from "../HttpServerError.ts";
15
+ +import type { HttpServerRequest } from "../HttpServerRequest.ts";
16
+ +import type { HttpServerResponse } from "../HttpServerResponse.ts";
17
+ +export declare const requestPreResponseHandlers: WeakMap<object, (request: HttpServerRequest, response: HttpServerResponse) => Effect.Effect<HttpServerResponse, HttpServerError>>;
18
+ +export declare const appendPreResponseHandlerUnsafe: (request: HttpServerRequest, handler: (request: HttpServerRequest, response: HttpServerResponse) => Effect.Effect<HttpServerResponse, HttpServerError>) => void;
@@ -8,6 +8,8 @@ minimumReleaseAgeStrict: true
8
8
  minimumReleaseAgeIgnoreMissingTime: false
9
9
  minimumReleaseAgeExclude:
10
10
  - '@bleedingdev/modern-js-*'
11
+ - 'effect@{{effectVersion}}'
12
+ - '@effect/opentelemetry@{{effectVersion}}'
11
13
  - '@tanstack/react-router'
12
14
  - '@tanstack/router-core'
13
15
  - 'typescript'
@@ -25,6 +27,7 @@ minimumReleaseAgeExclude:
25
27
  trustPolicy: no-downgrade
26
28
  trustPolicyIgnoreAfter: 1440
27
29
  trustPolicyExclude:
30
+ - 'effect@{{effectVersion}}'
28
31
  - '@effect/opentelemetry@{{effectVersion}}'
29
32
  blockExoticSubdeps: true
30
33
  engineStrict: true
@@ -45,11 +48,13 @@ peerDependencyRules:
45
48
  overrides:
46
49
  '@tanstack/react-router': {{tanstackRouterVersion}}
47
50
  '@tanstack/router-core': {{tanstackRouterCoreVersion}}
51
+ '@effect/opentelemetry': {{effectVersion}}
48
52
  '@effect/vitest': {{effectVitestVersion}}
49
53
  effect: {{effectVersion}}
50
54
  node-fetch: '{{nodeFetchVersion}}'
51
55
 
52
56
  allowBuilds:
57
+ '@parcel/watcher': true
53
58
  '@swc/core': true
54
59
  core-js: true
55
60
  esbuild: true
@@ -59,4 +64,7 @@ allowBuilds:
59
64
  workerd: true
60
65
 
61
66
  patchedDependencies:
67
+ '@module-federation/modern-js-v3@{{moduleFederationVersion}}': patches/@module-federation__modern-js-v3@{{moduleFederationVersion}}.patch
68
+ '@module-federation/bridge-react@{{moduleFederationVersion}}': patches/@module-federation__bridge-react@{{moduleFederationVersion}}.patch
62
69
  '@tanstack/router-core@{{tanstackRouterCoreVersion}}': patches/@tanstack__router-core@{{tanstackRouterCoreVersion}}.patch
70
+ 'effect@{{effectVersion}}': patches/effect-schema-error-type-id.patch
@@ -7,8 +7,14 @@ import { fileURLToPath } from 'node:url';
7
7
  const root = process.cwd();
8
8
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
9
9
  const templateWorkspaceDir = path.resolve(scriptDir, '..');
10
+ // Vendored skills always ship under template-workspace/.codex/skills; the
11
+ // workspace-side lockfile may live under .agents/ (agents-standard layout) or
12
+ // .codex/ (legacy default).
10
13
  const vendoredSkillsDir = path.join(templateWorkspaceDir, '.codex/skills');
11
- const lockPath = path.join(root, '.codex/skills-lock.json');
14
+ const lockRoot = fs.existsSync(path.join(root, '.agents/skills-lock.json'))
15
+ ? '.agents'
16
+ : '.codex';
17
+ const lockPath = path.join(root, `${lockRoot}/skills-lock.json`);
12
18
  const checkOnly = process.argv.includes('--check');
13
19
  const postinstall = process.argv.includes('--postinstall');
14
20
  const truthy = value => /^(1|true|yes|on)$/i.test(String(value ?? ''));
@@ -175,12 +181,14 @@ const resolveSkillDir = (sourceRoot, skillName) => {
175
181
  };
176
182
 
177
183
  if (!fs.existsSync(lockPath)) {
178
- console.error('Missing .codex/skills-lock.json');
184
+ console.error(
185
+ 'Missing skills-lock.json: expected .agents/skills-lock.json or .codex/skills-lock.json',
186
+ );
179
187
  process.exit(1);
180
188
  }
181
189
 
182
190
  const lock = readJson(lockPath);
183
- const installDir = path.join(root, lock.installDir ?? '.codex/skills');
191
+ const installDir = path.join(root, lock.installDir ?? `${lockRoot}/skills`);
184
192
  const sources = lock.sources ?? [];
185
193
  const vendoredSources = sources.filter(source => source.install === 'vendored');
186
194
  const cloneSources = sources.filter(source => source.install === 'clone');
@@ -189,6 +189,7 @@ if (exists('apps/shell-super-app') && verticalDirectories.length > 0) {
189
189
 
190
190
  function assertApiSurface(appPath) {
191
191
  const apiEntry = `${appPath}/api/index.ts`;
192
+ const backendEffectExpose = `${appPath}/api/effect-api.ts`;
192
193
  const sharedApi = `${appPath}/shared/api.ts`;
193
194
  const srcApiDirectory = `${appPath}/src/api`;
194
195
  const modernConfig = `${appPath}/modern.config.ts`;
@@ -235,8 +236,45 @@ function assertApiSurface(appPath) {
235
236
  'must import the contract from ../shared/api.ts.',
236
237
  );
237
238
  }
238
-
239
- if (exists(sharedApi)) {
239
+ if (exists(backendEffectExpose)) {
240
+ const backendExpose = readText(backendEffectExpose);
241
+ assertContains(
242
+ backendEffectExpose,
243
+ backendExpose,
244
+ /backendFederationContract/u,
245
+ 'must export backendFederationContract metadata.',
246
+ );
247
+ assertContains(
248
+ backendEffectExpose,
249
+ backendExpose,
250
+ /role:\s*['"]microvertical-server['"]/u,
251
+ 'must describe the MicroVertical server role.',
252
+ );
253
+ assertContains(
254
+ backendEffectExpose,
255
+ backendExpose,
256
+ /strictEffectApproach:\s*true/u,
257
+ 'must preserve strict Effect backend execution.',
258
+ );
259
+ assertContains(
260
+ backendEffectExpose,
261
+ backendExpose,
262
+ /contractVersion:\s*['"]microvertical-server-effect-v1['"]/u,
263
+ 'must preserve the MicroVertical server contract version.',
264
+ );
265
+ assertContains(
266
+ backendEffectExpose,
267
+ backendExpose,
268
+ /runtime\s*=\s*apiRuntime/u,
269
+ 'must export the generated Effect BFF runtime.',
270
+ );
271
+ assert(
272
+ !/\b(request|handler)\s*:\s*async\s*\(/u.test(backendExpose),
273
+ `${backendEffectExpose}: must not expose raw request handlers.`,
274
+ );
275
+ }
276
+
277
+ if (exists(sharedApi)) {
240
278
  const contract = readText(sharedApi);
241
279
  assertContains(
242
280
  sharedApi,
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const workspaceRoot = path.resolve(
7
+ process.env.ULTRAMODERN_WORKSPACE_ROOT ??
8
+ path.join(path.dirname(fileURLToPath(import.meta.url)), '../..'),
9
+ );
10
+ const configPath = path.join(workspaceRoot, '.modernjs/ultramodern.json');
11
+ const contractVersion = 'microvertical-server-effect-v1';
12
+ const nodeAdapterVersion = 'backend-mf-effect-v1';
13
+
14
+ function readJson(filePath) {
15
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
16
+ }
17
+
18
+ function readBuildIdentity(app) {
19
+ const buildModulePath = path.join(
20
+ workspaceRoot,
21
+ app.path,
22
+ 'shared/ultramodern-build.ts',
23
+ );
24
+ if (!fs.existsSync(buildModulePath)) {
25
+ return {};
26
+ }
27
+
28
+ const source = fs.readFileSync(buildModulePath, 'utf8');
29
+ return {
30
+ buildVersion: source.match(/\bbuild:\s*['"]([^'"]+)['"]/u)?.[1],
31
+ packageName: source.match(/\bpackageName:\s*['"]([^'"]+)['"]/u)?.[1],
32
+ version: source.match(/\bversion:\s*['"]([^'"]+)['"]/u)?.[1],
33
+ unitId: source.match(/\bunitId:\s*['"]([^'"]+)['"]/u)?.[1],
34
+ sourceRevision: source.match(/\bsourceRevision:\s*['"]([^'"]+)['"]/u)?.[1],
35
+ };
36
+ }
37
+
38
+ function parseArgs(argv) {
39
+ const options = {
40
+ app: undefined,
41
+ target: 'dist',
42
+ };
43
+ for (let index = 0; index < argv.length; index += 1) {
44
+ const arg = argv[index];
45
+ if (arg === '--app') {
46
+ options.app = argv[index + 1];
47
+ index += 1;
48
+ continue;
49
+ }
50
+ if (arg === '--target') {
51
+ options.target = argv[index + 1] ?? options.target;
52
+ index += 1;
53
+ continue;
54
+ }
55
+ if (arg === '--help' || arg === '-h') {
56
+ console.log(
57
+ 'Usage: node scripts/generate-node-backend-federation.mts [--app <id>] [--target dist|dist-cloudflare]',
58
+ );
59
+ process.exit(0);
60
+ }
61
+ }
62
+ return options;
63
+ }
64
+
65
+ function backendApps(config, appFilter) {
66
+ return (config.topology?.apps ?? []).filter(app => {
67
+ if (app.kind !== 'vertical') {
68
+ return false;
69
+ }
70
+ if (!backendFederationMetadata(app)) {
71
+ return false;
72
+ }
73
+ return appFilter ? app.id === appFilter : true;
74
+ });
75
+ }
76
+
77
+ function backendFederationMetadata(app) {
78
+ return app.backendFederation ?? app.api?.backendFederation;
79
+ }
80
+
81
+ function backendFederationExposes(app) {
82
+ const backend = backendFederationMetadata(app);
83
+ const exposes = Array.isArray(backend.exposes)
84
+ ? backend.exposes.filter(expose => typeof expose === 'string')
85
+ : backend.exposes &&
86
+ typeof backend.exposes === 'object' &&
87
+ !Array.isArray(backend.exposes)
88
+ ? Object.keys(backend.exposes)
89
+ : [];
90
+
91
+ if (exposes.length > 0) {
92
+ return exposes;
93
+ }
94
+
95
+ if (typeof backend.expose === 'string') {
96
+ return [backend.expose];
97
+ }
98
+
99
+ throw new Error(`${app.id} backend federation expose metadata is missing`);
100
+ }
101
+
102
+ function normalizeBackendFederation(app) {
103
+ const backend = backendFederationMetadata(app);
104
+ const nodeSurface = backend.executionSurfaces?.node ?? {};
105
+ const exposes = backendFederationExposes(app);
106
+
107
+ return {
108
+ ...backend,
109
+ name: backend.name ?? nodeSurface.remoteName,
110
+ remoteType: backend.remoteType ?? nodeSurface.remoteType ?? 'module',
111
+ manifestUrl: backend.manifestUrl ?? nodeSurface.manifestUrl,
112
+ containerEntry: backend.containerEntry ?? nodeSurface.containerEntry,
113
+ runtimePackage: backend.runtimePackage ?? nodeSurface.runtimePackage,
114
+ contractVersion:
115
+ backend.contractVersion ??
116
+ backend.compatibility?.contractVersion ??
117
+ contractVersion,
118
+ nodeAdapterVersion:
119
+ backend.nodeAdapterVersion ?? nodeSurface.adapterVersion ?? nodeAdapterVersion,
120
+ expose:
121
+ typeof backend.expose === 'string'
122
+ ? backend.expose
123
+ : (nodeSurface.expose ?? exposes[0]),
124
+ exposes,
125
+ };
126
+ }
127
+
128
+ function createRemoteEntrySource(app) {
129
+ const exposes = backendFederationExposes(app);
130
+ return `const factories = {
131
+ ${exposes
132
+ .map(
133
+ expose => `${JSON.stringify(expose)}: async () =>
134
+ import(new URL('../api/backend-federation.ts', import.meta.url).href),
135
+ `,
136
+ )
137
+ .join('')}
138
+ };
139
+
140
+ export async function init() {}
141
+
142
+ export function get(id) {
143
+ const factory = factories[id];
144
+ if (!factory) {
145
+ throw new Error(\`Unknown backend federation expose \${id}\`);
146
+ }
147
+ return factory;
148
+ }
149
+
150
+ export default { init, get };
151
+ `;
152
+ }
153
+
154
+ function createManifest(app) {
155
+ const backend = normalizeBackendFederation(app);
156
+ const buildIdentity = readBuildIdentity(app);
157
+ const compactDeliveryUnit =
158
+ app.deliveryUnit && typeof app.deliveryUnit === 'object'
159
+ ? app.deliveryUnit
160
+ : undefined;
161
+ const unitId = compactDeliveryUnit?.unitId ?? buildIdentity.unitId;
162
+ const sourceRevision =
163
+ compactDeliveryUnit?.sourceRevision ?? buildIdentity.sourceRevision;
164
+ return {
165
+ schemaVersion: 1,
166
+ name: backend.name,
167
+ id: backend.name,
168
+ version: buildIdentity.version,
169
+ buildVersion: buildIdentity.buildVersion,
170
+ type: backend.remoteType,
171
+ entry: {
172
+ url: backend.containerEntry,
173
+ type: backend.remoteType,
174
+ },
175
+ metaData: {
176
+ name: backend.name,
177
+ type: backend.remoteType,
178
+ buildInfo: {
179
+ buildName: buildIdentity.packageName,
180
+ buildVersion: buildIdentity.buildVersion,
181
+ },
182
+ remoteEntry: {
183
+ name: 'backendRemoteEntry.mjs',
184
+ path: '',
185
+ type: backend.remoteType,
186
+ },
187
+ exposes: backend.exposes.map(expose => ({
188
+ id: expose,
189
+ name: expose,
190
+ path: expose,
191
+ })),
192
+ },
193
+ exposes: backend.exposes.map(expose => ({
194
+ id: expose,
195
+ name: expose,
196
+ path: expose,
197
+ })),
198
+ backendFederation: {
199
+ role: backend.role ?? 'microvertical-server',
200
+ name: backend.name,
201
+ runtimeFramework: backend.runtimeFramework ?? 'effect',
202
+ strictEffectApproach: backend.strictEffectApproach ?? true,
203
+ contractVersion: backend.contractVersion,
204
+ nodeAdapterVersion: backend.nodeAdapterVersion,
205
+ manifestUrl: backend.manifestUrl,
206
+ containerEntry: backend.containerEntry,
207
+ expose: backend.expose,
208
+ runtimePackage: backend.runtimePackage,
209
+ ...(unitId && buildIdentity.buildVersion
210
+ ? {
211
+ deliveryUnit: {
212
+ schemaVersion: 1,
213
+ kind: 'microvertical-delivery-unit',
214
+ unitId,
215
+ packageName: buildIdentity.packageName,
216
+ version: buildIdentity.version,
217
+ buildMarker: buildIdentity.buildVersion,
218
+ sourceRevision,
219
+ },
220
+ }
221
+ : {}),
222
+ versionBoundary: {
223
+ packageName: buildIdentity.packageName,
224
+ version: buildIdentity.version,
225
+ buildVersion: buildIdentity.buildVersion,
226
+ ...(unitId && buildIdentity.buildVersion
227
+ ? {
228
+ deliveryUnit: {
229
+ unitId,
230
+ buildMarker: buildIdentity.buildVersion,
231
+ },
232
+ }
233
+ : {}),
234
+ },
235
+ },
236
+ };
237
+ }
238
+
239
+ const options = parseArgs(process.argv.slice(2));
240
+ const config = readJson(configPath);
241
+ const apps = backendApps(config, options.app);
242
+
243
+ if (options.app && apps.length === 0) {
244
+ throw new Error(`No generated backend federation app matched ${options.app}`);
245
+ }
246
+
247
+ for (const app of apps) {
248
+ const outputDir = path.join(workspaceRoot, app.path, options.target);
249
+ fs.mkdirSync(outputDir, { recursive: true });
250
+ fs.writeFileSync(
251
+ path.join(outputDir, 'backendRemoteEntry.mjs'),
252
+ createRemoteEntrySource(app),
253
+ 'utf-8',
254
+ );
255
+ fs.writeFileSync(
256
+ path.join(outputDir, 'backend-mf-manifest.json'),
257
+ `${JSON.stringify(createManifest(app), null, 2)}\n`,
258
+ 'utf-8',
259
+ );
260
+ console.log(
261
+ `[ultramodern] generated backend federation artifacts for ${app.id} in ${path.relative(
262
+ workspaceRoot,
263
+ outputDir,
264
+ )}`,
265
+ );
266
+ }
267
+
268
+ if (apps.length === 0) {
269
+ console.log('[ultramodern] no backend federation apps to generate');
270
+ }