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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/cjs/ultramodern-tooling/commands.cjs +497 -155
  2. package/dist/cjs/ultramodern-tooling/config.cjs +149 -8
  3. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +25 -0
  4. package/dist/cjs/ultramodern-workspace/api.cjs +44 -0
  5. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +282 -0
  6. package/dist/cjs/ultramodern-workspace/contracts.cjs +24 -8
  7. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +157 -0
  8. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +88 -0
  9. package/dist/cjs/ultramodern-workspace/descriptors.cjs +24 -0
  10. package/dist/cjs/ultramodern-workspace/module-federation.cjs +122 -44
  11. package/dist/cjs/ultramodern-workspace/package-json.cjs +11 -34
  12. package/dist/cjs/ultramodern-workspace/pnpm-workspace-policy-plan.cjs +166 -0
  13. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +153 -0
  14. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +175 -0
  15. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +43 -62
  16. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +97 -0
  17. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +9 -3
  18. package/dist/cjs/ultramodern-workspace/zerops.cjs +100 -0
  19. package/dist/esm/ultramodern-tooling/commands.js +500 -158
  20. package/dist/esm/ultramodern-tooling/config.js +144 -9
  21. package/dist/esm/ultramodern-workspace/add-vertical.js +26 -1
  22. package/dist/esm/ultramodern-workspace/api.js +43 -2
  23. package/dist/esm/ultramodern-workspace/backend-federation.js +217 -0
  24. package/dist/esm/ultramodern-workspace/contracts.js +24 -8
  25. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +108 -0
  26. package/dist/esm/ultramodern-workspace/delivery-unit.js +31 -0
  27. package/dist/esm/ultramodern-workspace/descriptors.js +13 -1
  28. package/dist/esm/ultramodern-workspace/module-federation.js +121 -36
  29. package/dist/esm/ultramodern-workspace/package-json.js +11 -31
  30. package/dist/esm/ultramodern-workspace/pnpm-workspace-policy-plan.js +113 -0
  31. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
  32. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +110 -0
  33. package/dist/esm/ultramodern-workspace/workspace-scripts.js +35 -63
  34. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +59 -0
  35. package/dist/esm/ultramodern-workspace/write-workspace.js +11 -5
  36. package/dist/esm/ultramodern-workspace/zerops.js +62 -0
  37. package/dist/esm-node/ultramodern-tooling/commands.js +500 -158
  38. package/dist/esm-node/ultramodern-tooling/config.js +144 -9
  39. package/dist/esm-node/ultramodern-workspace/add-vertical.js +26 -1
  40. package/dist/esm-node/ultramodern-workspace/api.js +43 -2
  41. package/dist/esm-node/ultramodern-workspace/backend-federation.js +218 -0
  42. package/dist/esm-node/ultramodern-workspace/contracts.js +24 -8
  43. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +109 -0
  44. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +32 -0
  45. package/dist/esm-node/ultramodern-workspace/descriptors.js +13 -1
  46. package/dist/esm-node/ultramodern-workspace/module-federation.js +121 -36
  47. package/dist/esm-node/ultramodern-workspace/package-json.js +11 -31
  48. package/dist/esm-node/ultramodern-workspace/pnpm-workspace-policy-plan.js +114 -0
  49. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
  50. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +111 -0
  51. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +35 -63
  52. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +60 -0
  53. package/dist/esm-node/ultramodern-workspace/write-workspace.js +11 -5
  54. package/dist/esm-node/ultramodern-workspace/zerops.js +63 -0
  55. package/dist/types/ultramodern-tooling/config.d.ts +6 -0
  56. package/dist/types/ultramodern-workspace/api.d.ts +1 -0
  57. package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
  58. package/dist/types/ultramodern-workspace/contracts.d.ts +1 -1
  59. package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
  60. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +27 -0
  61. package/dist/types/ultramodern-workspace/descriptors.d.ts +4 -0
  62. package/dist/types/ultramodern-workspace/module-federation.d.ts +4 -9
  63. package/dist/types/ultramodern-workspace/package-json.d.ts +0 -1
  64. package/dist/types/ultramodern-workspace/pnpm-workspace-policy-plan.d.ts +39 -0
  65. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
  66. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +72 -0
  67. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +3 -0
  68. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +42 -0
  69. package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
  70. package/package.json +3 -3
  71. package/template-workspace/.gitignore.handlebars +1 -0
  72. package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
  73. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
  74. package/templates/workspace-scripts/generate-node-backend-federation.mjs +270 -0
  75. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +448 -0
  76. package/templates/workspace-scripts/proof-cloudflare-version.mjs +234 -8
  77. package/templates/workspace-scripts/proof-node-backend-federation.mjs +729 -0
  78. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
  79. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +322 -8
@@ -0,0 +1,729 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+
7
+ const workspaceRoot = path.resolve(process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd());
8
+ const workspaceRequire = createRequire(path.join(workspaceRoot, 'package.json'));
9
+ const compactConfigPath = path.join(workspaceRoot, '.modernjs/ultramodern.json');
10
+ const defaultOut = path.join(
11
+ workspaceRoot,
12
+ '.codex/reports/node-backend-federation-proof/proof.json',
13
+ );
14
+
15
+ const contractVersion = 'microvertical-server-effect-v1';
16
+ const nodeAdapterVersion = 'backend-mf-effect-v1';
17
+ const backendExpose = './effect-api';
18
+ const localRuntimeRelativePaths = [
19
+ 'packages/cli/plugin-bff/dist/esm-node/runtime/effect/index.mjs',
20
+ 'cli/plugin-bff/dist/esm-node/runtime/effect/index.mjs',
21
+ ];
22
+
23
+ function readJson(filePath) {
24
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
25
+ }
26
+
27
+ function readBuildIdentity(app) {
28
+ const buildModulePath = path.join(
29
+ workspaceRoot,
30
+ app.directory,
31
+ 'shared/ultramodern-build.ts',
32
+ );
33
+ if (!fs.existsSync(buildModulePath)) {
34
+ return {};
35
+ }
36
+
37
+ const source = fs.readFileSync(buildModulePath, 'utf8');
38
+ return {
39
+ buildVersion: source.match(/\bbuild:\s*['"]([^'"]+)['"]/u)?.[1],
40
+ packageName: source.match(/\bpackageName:\s*['"]([^'"]+)['"]/u)?.[1],
41
+ version: source.match(/\bversion:\s*['"]([^'"]+)['"]/u)?.[1],
42
+ unitId: source.match(/\bunitId:\s*['"]([^'"]+)['"]/u)?.[1],
43
+ sourceRevision: source.match(/\bsourceRevision:\s*['"]([^'"]+)['"]/u)?.[1],
44
+ };
45
+ }
46
+
47
+ function hasBackendFederationManifestAdapter(runtime) {
48
+ return typeof runtime?.loadBackendFederatedEffectApiFromManifest === 'function';
49
+ }
50
+
51
+ function findLocalRuntimePath(createBin) {
52
+ let current = path.dirname(fileURLToPath(pathToFileURL(createBin)));
53
+ for (let depth = 0; depth < 8; depth += 1) {
54
+ for (const relativePath of localRuntimeRelativePaths) {
55
+ const candidate = path.join(current, relativePath);
56
+ if (fs.existsSync(candidate)) {
57
+ return candidate;
58
+ }
59
+ }
60
+
61
+ const parent = path.dirname(current);
62
+ if (parent === current) {
63
+ break;
64
+ }
65
+ current = parent;
66
+ }
67
+
68
+ return undefined;
69
+ }
70
+
71
+ async function importBackendFederationRuntime() {
72
+ let importError;
73
+ try {
74
+ const runtimePath = workspaceRequire.resolve('@modern-js/plugin-bff/effect');
75
+ const runtime = await import(pathToFileURL(runtimePath).href);
76
+ if (hasBackendFederationManifestAdapter(runtime)) {
77
+ return runtime;
78
+ }
79
+
80
+ importError = new Error(
81
+ `${runtimePath} does not export loadBackendFederatedEffectApiFromManifest`,
82
+ );
83
+ } catch (error) {
84
+ importError = error;
85
+ }
86
+
87
+ const createBin = process.env.ULTRAMODERN_CREATE_BIN;
88
+ if (!createBin) {
89
+ throw importError;
90
+ }
91
+
92
+ const localRuntimePath = findLocalRuntimePath(createBin);
93
+ if (!localRuntimePath) {
94
+ throw importError;
95
+ }
96
+
97
+ const localRuntime = await import(pathToFileURL(localRuntimePath).href);
98
+ if (!hasBackendFederationManifestAdapter(localRuntime)) {
99
+ throw new Error(
100
+ `${localRuntimePath} does not export loadBackendFederatedEffectApiFromManifest`,
101
+ );
102
+ }
103
+
104
+ return localRuntime;
105
+ }
106
+
107
+ function normalizeRelativePath(value) {
108
+ return String(value ?? '')
109
+ .replace(/\\/gu, '/')
110
+ .replace(/^\.\/+/u, '');
111
+ }
112
+
113
+ function toPascalCase(value) {
114
+ return String(value)
115
+ .split(/[^a-zA-Z0-9]+/u)
116
+ .filter(Boolean)
117
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
118
+ .join('');
119
+ }
120
+
121
+ function createBackendName(app) {
122
+ return (
123
+ app.backendFederation?.name ??
124
+ app.backendFederation?.executionSurfaces?.node?.remoteName ??
125
+ app.serverExecution?.node?.remoteName ??
126
+ (typeof app.moduleFederation?.name === 'string'
127
+ ? `${app.moduleFederation.name}Backend`
128
+ : `vertical${toPascalCase(app.id)}Backend`)
129
+ );
130
+ }
131
+
132
+ function createBackendManifestUrl(app) {
133
+ return (
134
+ app.backendFederation?.executionSurfaces?.node?.manifestUrl ??
135
+ app.serverExecution?.node?.manifestUrl ??
136
+ `http://localhost:${app.port}/backend-mf-manifest.json`
137
+ );
138
+ }
139
+
140
+ function createBackendContainerEntry(app) {
141
+ return (
142
+ app.backendFederation?.executionSurfaces?.node?.containerEntry ??
143
+ app.serverExecution?.node?.containerEntry ??
144
+ `http://localhost:${app.port}/backendRemoteEntry.mjs`
145
+ );
146
+ }
147
+
148
+ function resolveRemoteType(app) {
149
+ return (
150
+ app.backendFederation?.executionSurfaces?.node?.remoteType ??
151
+ app.serverExecution?.node?.remoteType ??
152
+ 'module'
153
+ );
154
+ }
155
+
156
+ function normalizeRoutePath(value) {
157
+ const route = String(value ?? '').trim();
158
+ if (!route) {
159
+ return '/';
160
+ }
161
+ return route.startsWith('/') ? route : `/${route}`;
162
+ }
163
+
164
+ function collectJsonSmokeChecks(apps, targetApp) {
165
+ const apiPrefix = normalizeRoutePath(targetApp.api?.prefix ?? `/${targetApp.id}-api`);
166
+
167
+ return apps
168
+ .flatMap((app) =>
169
+ Array.isArray(app?.deploy?.cloudflare?.jsonSmokeChecks)
170
+ ? app.deploy.cloudflare.jsonSmokeChecks
171
+ : [],
172
+ )
173
+ .filter((check) => {
174
+ if (typeof check?.route !== 'string') {
175
+ return false;
176
+ }
177
+ const route = normalizeRoutePath(check.route);
178
+ return route === apiPrefix || route.startsWith(`${apiPrefix}/`);
179
+ });
180
+ }
181
+
182
+ function compactApps(config, appFilter) {
183
+ const apps = Array.isArray(config.topology?.apps) ? config.topology.apps : [];
184
+ const filteredApps = apps
185
+ .filter((app) => app?.kind === 'vertical' && app.api)
186
+ .filter((app) => !appFilter || app.id === appFilter)
187
+ .map((app) => ({
188
+ id: app.id,
189
+ directory:
190
+ typeof app.path === 'string' ? normalizeRelativePath(app.path) : `verticals/${app.id}`,
191
+ backendName: createBackendName(app),
192
+ manifestUrl: createBackendManifestUrl(app),
193
+ containerEntry: createBackendContainerEntry(app),
194
+ remoteType: resolveRemoteType(app),
195
+ smokeChecks: collectJsonSmokeChecks(apps, app),
196
+ compactDeliveryUnit:
197
+ app.deliveryUnit && typeof app.deliveryUnit === 'object'
198
+ ? app.deliveryUnit
199
+ : undefined,
200
+ }));
201
+
202
+ if (appFilter && filteredApps.length === 0) {
203
+ throw new Error(`No vertical API app matched --app ${appFilter}`);
204
+ }
205
+
206
+ return filteredApps;
207
+ }
208
+
209
+ function assertEqual(actual, expected, message) {
210
+ if (actual !== expected) {
211
+ throw new Error(`${message}: expected ${expected}, received ${actual}`);
212
+ }
213
+ }
214
+
215
+ function assertFile(filePath, appId, label) {
216
+ if (!fs.existsSync(filePath)) {
217
+ throw new Error(
218
+ `${appId} missing ${path.relative(
219
+ workspaceRoot,
220
+ filePath,
221
+ )}; run pnpm build or the relevant vertical build before pnpm node:proof`,
222
+ );
223
+ }
224
+
225
+ if (!fs.statSync(filePath).isFile()) {
226
+ throw new Error(`${appId} ${label} is not a file`);
227
+ }
228
+ }
229
+
230
+ function jsonPathValue(value, path) {
231
+ const segments = String(path ?? '')
232
+ .split('.')
233
+ .filter(Boolean);
234
+ let current = value;
235
+ for (const segment of segments) {
236
+ if (current === null || current === undefined) {
237
+ return undefined;
238
+ }
239
+ if (Array.isArray(current) && /^\d+$/u.test(segment)) {
240
+ current = current[Number(segment)];
241
+ continue;
242
+ }
243
+ if (typeof current !== 'object' || !Object.hasOwn(current, segment)) {
244
+ return undefined;
245
+ }
246
+ current = current[segment];
247
+ }
248
+ return current;
249
+ }
250
+
251
+ function normalizeJsonExpectations(check) {
252
+ if (Array.isArray(check.expectations)) {
253
+ return check.expectations;
254
+ }
255
+ if (check.expect && typeof check.expect === 'object') {
256
+ return Object.entries(check.expect).map(([path, value]) => ({
257
+ path,
258
+ value,
259
+ }));
260
+ }
261
+ return [];
262
+ }
263
+
264
+ function assertJsonEqual(actual, expected, message) {
265
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
266
+ throw new Error(
267
+ `${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}`,
268
+ );
269
+ }
270
+ }
271
+
272
+ async function runSmokeChecks(app, loaded, createEffectBffTestHandler) {
273
+ if (!Array.isArray(app.smokeChecks) || app.smokeChecks.length === 0) {
274
+ return [];
275
+ }
276
+ if (typeof createEffectBffTestHandler !== 'function') {
277
+ throw new Error(`${app.id} backend runtime cannot create Effect test handler`);
278
+ }
279
+
280
+ const servicePrefix = loaded.contract?.servicePrefix;
281
+ if (typeof servicePrefix !== 'string' || servicePrefix.length === 0) {
282
+ throw new Error(`${app.id} backend expose missing contract.servicePrefix`);
283
+ }
284
+
285
+ const edge = await createEffectBffTestHandler({
286
+ module: loaded.runtime,
287
+ prefix: servicePrefix,
288
+ });
289
+ try {
290
+ const results = [];
291
+ for (const check of app.smokeChecks) {
292
+ const method = String(check.method ?? 'GET').toUpperCase();
293
+ const route = normalizeRoutePath(check.route);
294
+ const headers = {};
295
+ const init = { method, headers };
296
+ if (check.body !== undefined) {
297
+ headers['content-type'] = 'application/json';
298
+ init.body = JSON.stringify(check.body);
299
+ }
300
+
301
+ const response = await edge.handler(new Request(new URL(route, 'http://localhost'), init));
302
+ const bodyText = await response.text();
303
+ let body;
304
+ try {
305
+ body = bodyText.length > 0 ? JSON.parse(bodyText) : undefined;
306
+ } catch (error) {
307
+ throw new Error(`${app.id} ${method} ${route} did not return JSON: ${error.message}`);
308
+ }
309
+
310
+ if (!response.ok) {
311
+ throw new Error(`${app.id} ${method} ${route} returned HTTP ${response.status}`);
312
+ }
313
+
314
+ const assertions = normalizeJsonExpectations(check).map((expectation) => {
315
+ const actual = jsonPathValue(body, expectation.path);
316
+ assertJsonEqual(
317
+ actual,
318
+ expectation.value,
319
+ `${app.id} ${method} ${route} ${expectation.path}`,
320
+ );
321
+ return {
322
+ path: expectation.path,
323
+ expected: expectation.value,
324
+ actual,
325
+ status: 'pass',
326
+ };
327
+ });
328
+
329
+ results.push({
330
+ id: check.id ?? `${method} ${route}`,
331
+ method,
332
+ route,
333
+ statusCode: response.status,
334
+ assertions,
335
+ status: 'pass',
336
+ });
337
+ }
338
+ return results;
339
+ } finally {
340
+ await edge.dispose?.();
341
+ }
342
+ }
343
+
344
+ function resolveArtifacts(app, target) {
345
+ const targetDirectory = path.join(workspaceRoot, app.directory, target);
346
+
347
+ return {
348
+ manifestPath: path.join(targetDirectory, 'backend-mf-manifest.json'),
349
+ entryPath: path.join(targetDirectory, 'backendRemoteEntry.mjs'),
350
+ };
351
+ }
352
+
353
+ function validateManifest(app, manifest, buildIdentity) {
354
+ assertEqual(manifest.schemaVersion, 1, `${app.id} backend manifest schema`);
355
+ assertEqual(manifest.name, app.backendName, `${app.id} backend manifest name`);
356
+ assertEqual(manifest.id, app.backendName, `${app.id} backend manifest id`);
357
+ assertEqual(
358
+ manifest.version,
359
+ buildIdentity.version,
360
+ `${app.id} backend manifest version`,
361
+ );
362
+ assertEqual(
363
+ manifest.buildVersion,
364
+ buildIdentity.buildVersion,
365
+ `${app.id} backend manifest build version`,
366
+ );
367
+ assertEqual(manifest.metaData?.name, app.backendName, `${app.id} backend manifest metadata name`);
368
+ assertEqual(
369
+ manifest.metaData?.buildInfo?.buildName,
370
+ buildIdentity.packageName,
371
+ `${app.id} backend manifest package name`,
372
+ );
373
+ assertEqual(
374
+ manifest.metaData?.buildInfo?.buildVersion,
375
+ buildIdentity.buildVersion,
376
+ `${app.id} backend manifest metadata build version`,
377
+ );
378
+ assertEqual(
379
+ manifest.metaData?.remoteEntry?.name,
380
+ 'backendRemoteEntry.mjs',
381
+ `${app.id} backend manifest remote entry filename`,
382
+ );
383
+ assertEqual(
384
+ manifest.metaData?.remoteEntry?.type,
385
+ app.remoteType,
386
+ `${app.id} backend manifest remote entry type`,
387
+ );
388
+ assertEqual(manifest.entry?.url, app.containerEntry, `${app.id} backend manifest entry URL`);
389
+ assertEqual(manifest.entry?.type, app.remoteType, `${app.id} backend manifest entry type`);
390
+ assertEqual(
391
+ manifest.backendFederation?.role,
392
+ 'microvertical-server',
393
+ `${app.id} backend manifest role`,
394
+ );
395
+ assertEqual(
396
+ manifest.backendFederation?.runtimeFramework,
397
+ 'effect',
398
+ `${app.id} backend manifest runtime framework`,
399
+ );
400
+ assertEqual(
401
+ manifest.backendFederation?.strictEffectApproach,
402
+ true,
403
+ `${app.id} backend manifest strict Effect flag`,
404
+ );
405
+ assertEqual(
406
+ manifest.backendFederation?.contractVersion,
407
+ contractVersion,
408
+ `${app.id} backend manifest contract version`,
409
+ );
410
+ assertEqual(
411
+ manifest.backendFederation?.nodeAdapterVersion,
412
+ nodeAdapterVersion,
413
+ `${app.id} backend manifest Node adapter version`,
414
+ );
415
+ assertEqual(
416
+ manifest.backendFederation?.manifestUrl,
417
+ app.manifestUrl,
418
+ `${app.id} backend manifest URL`,
419
+ );
420
+ assertEqual(
421
+ manifest.backendFederation?.containerEntry,
422
+ app.containerEntry,
423
+ `${app.id} backend container URL`,
424
+ );
425
+ assertEqual(manifest.backendFederation?.expose, backendExpose, `${app.id} backend expose`);
426
+ assertEqual(
427
+ manifest.backendFederation?.versionBoundary?.packageName,
428
+ buildIdentity.packageName,
429
+ `${app.id} backend manifest version-boundary package`,
430
+ );
431
+ assertEqual(
432
+ manifest.backendFederation?.versionBoundary?.version,
433
+ buildIdentity.version,
434
+ `${app.id} backend manifest version-boundary version`,
435
+ );
436
+ assertEqual(
437
+ manifest.backendFederation?.versionBoundary?.buildVersion,
438
+ buildIdentity.buildVersion,
439
+ `${app.id} backend manifest version-boundary build version`,
440
+ );
441
+
442
+ const manifestDeliveryUnit = manifest.backendFederation?.deliveryUnit;
443
+ if (manifestDeliveryUnit) {
444
+ assertEqual(
445
+ manifestDeliveryUnit.unitId,
446
+ buildIdentity.unitId,
447
+ `${app.id} backend manifest delivery-unit id`,
448
+ );
449
+ assertEqual(
450
+ manifestDeliveryUnit.buildMarker,
451
+ buildIdentity.buildVersion,
452
+ `${app.id} backend manifest delivery-unit build marker`,
453
+ );
454
+ assertEqual(
455
+ manifestDeliveryUnit.packageName,
456
+ buildIdentity.packageName,
457
+ `${app.id} backend manifest delivery-unit package name`,
458
+ );
459
+ assertEqual(
460
+ manifestDeliveryUnit.version,
461
+ buildIdentity.version,
462
+ `${app.id} backend manifest delivery-unit version`,
463
+ );
464
+ assertEqual(
465
+ manifestDeliveryUnit.sourceRevision,
466
+ buildIdentity.sourceRevision,
467
+ `${app.id} backend manifest delivery-unit source revision`,
468
+ );
469
+ }
470
+
471
+ const versionBoundaryDeliveryUnit =
472
+ manifest.backendFederation?.versionBoundary?.deliveryUnit;
473
+ if (versionBoundaryDeliveryUnit) {
474
+ assertEqual(
475
+ versionBoundaryDeliveryUnit.unitId,
476
+ buildIdentity.unitId,
477
+ `${app.id} backend manifest version-boundary delivery-unit id`,
478
+ );
479
+ assertEqual(
480
+ versionBoundaryDeliveryUnit.buildMarker,
481
+ buildIdentity.buildVersion,
482
+ `${app.id} backend manifest version-boundary delivery-unit build marker`,
483
+ );
484
+ }
485
+
486
+ const exposes = Array.isArray(manifest.exposes) ? manifest.exposes : [];
487
+ if (!exposes.some((expose) => expose?.name === backendExpose)) {
488
+ throw new Error(`${app.id} backend manifest missing ${backendExpose} expose`);
489
+ }
490
+ }
491
+
492
+ function assertCompactDeliveryUnitMatchesBuild(app, buildIdentity) {
493
+ const compactDeliveryUnit = app.compactDeliveryUnit;
494
+ if (!compactDeliveryUnit) {
495
+ return;
496
+ }
497
+
498
+ const mismatches = [];
499
+ const compare = (label, a, b) => {
500
+ if (a !== undefined && b !== undefined && a !== b) {
501
+ mismatches.push(`${label}: deliveryUnit=${a} vs ultramodern-build=${b}`);
502
+ }
503
+ };
504
+ compare('unitId', compactDeliveryUnit.unitId, buildIdentity.unitId);
505
+ compare(
506
+ 'buildMarker/build',
507
+ compactDeliveryUnit.buildMarker,
508
+ buildIdentity.buildVersion,
509
+ );
510
+ compare(
511
+ 'packageName',
512
+ compactDeliveryUnit.packageName,
513
+ buildIdentity.packageName,
514
+ );
515
+ compare('version', compactDeliveryUnit.version, buildIdentity.version);
516
+
517
+ if (mismatches.length > 0) {
518
+ throw new Error(
519
+ `${app.id} delivery-unit identity drift between ${path.relative(
520
+ workspaceRoot,
521
+ compactConfigPath,
522
+ )} (deliveryUnit) and ${path.relative(
523
+ workspaceRoot,
524
+ path.join(workspaceRoot, app.directory, 'shared/ultramodern-build.ts'),
525
+ )}: ${mismatches.join('; ')}`,
526
+ );
527
+ }
528
+ }
529
+
530
+ async function importBackendEntry(entryPath) {
531
+ const entry = await import(pathToFileURL(entryPath).href);
532
+ const container = entry.default ?? entry;
533
+
534
+ if (!container || typeof container.get !== 'function' || typeof container.init !== 'function') {
535
+ throw new Error(
536
+ `${path.relative(
537
+ workspaceRoot,
538
+ entryPath,
539
+ )} does not export a Module Federation get/init container`,
540
+ );
541
+ }
542
+
543
+ return container;
544
+ }
545
+
546
+ async function proveBackend(app, backendRuntime, target) {
547
+ const {
548
+ createEffectBffTestHandler,
549
+ loadBackendFederatedEffectApiFromManifest,
550
+ } = backendRuntime;
551
+ const { manifestPath, entryPath } = resolveArtifacts(app, target);
552
+
553
+ assertFile(manifestPath, app.id, 'backend manifest');
554
+ assertFile(entryPath, app.id, 'backend remote entry');
555
+
556
+ const buildIdentity = readBuildIdentity(app);
557
+ assertCompactDeliveryUnitMatchesBuild(app, buildIdentity);
558
+ const manifest = readJson(manifestPath);
559
+ validateManifest(app, manifest, buildIdentity);
560
+
561
+ const localRuntimeEntry = pathToFileURL(entryPath).href;
562
+ const loaded = await loadBackendFederatedEffectApiFromManifest({
563
+ hostName: 'ultramodernNodeBackendProof',
564
+ manifest,
565
+ manifestPath,
566
+ remote: {
567
+ entry: localRuntimeEntry,
568
+ },
569
+ expected: {
570
+ buildVersion: buildIdentity.buildVersion,
571
+ contractVersion,
572
+ nodeAdapterVersion,
573
+ packageName: buildIdentity.packageName,
574
+ remoteName: app.backendName,
575
+ },
576
+ });
577
+ const backendContract = loaded.backendFederationContract;
578
+
579
+ assertEqual(
580
+ backendContract?.strictEffectApproach,
581
+ true,
582
+ `${app.id} backend expose strict Effect flag`,
583
+ );
584
+ assertEqual(
585
+ backendContract?.runtimeFramework,
586
+ 'effect',
587
+ `${app.id} backend expose runtime framework`,
588
+ );
589
+ assertEqual(backendContract?.role, 'microvertical-server', `${app.id} backend expose role`);
590
+ assertEqual(backendContract?.name, app.backendName, `${app.id} backend expose name`);
591
+ assertEqual(
592
+ backendContract?.compatibility?.contractVersion,
593
+ contractVersion,
594
+ `${app.id} backend expose contract version`,
595
+ );
596
+ assertEqual(
597
+ backendContract?.compatibility?.nodeAdapterVersion,
598
+ nodeAdapterVersion,
599
+ `${app.id} backend expose Node adapter version`,
600
+ );
601
+ assertEqual(
602
+ backendContract?.compatibility?.packageName,
603
+ buildIdentity.packageName,
604
+ `${app.id} backend expose package name`,
605
+ );
606
+ assertEqual(
607
+ backendContract?.compatibility?.build,
608
+ buildIdentity.buildVersion,
609
+ `${app.id} backend expose build version`,
610
+ );
611
+ assertEqual(
612
+ manifest.buildVersion,
613
+ backendContract?.compatibility?.build,
614
+ `${app.id} backend manifest/expose build coupling`,
615
+ );
616
+ if (backendContract?.compatibility?.unitId !== undefined) {
617
+ assertEqual(
618
+ backendContract.compatibility.unitId,
619
+ manifest.backendFederation?.deliveryUnit?.unitId,
620
+ `${app.id} backend expose delivery-unit id`,
621
+ );
622
+ }
623
+
624
+ if (loaded.api === undefined || loaded.runtime === undefined) {
625
+ throw new Error(`${app.id} backend expose missing api/runtime exports`);
626
+ }
627
+
628
+ const smokeChecks = await runSmokeChecks(app, loaded, createEffectBffTestHandler);
629
+
630
+ return {
631
+ appId: app.id,
632
+ expose: backendExpose,
633
+ manifestPath: normalizeRelativePath(path.relative(workspaceRoot, manifestPath)),
634
+ containerPath: normalizeRelativePath(path.relative(workspaceRoot, entryPath)),
635
+ manifestUrl: app.manifestUrl,
636
+ containerEntry: app.containerEntry,
637
+ runtimeEntry: localRuntimeEntry,
638
+ remoteName: app.backendName,
639
+ remoteType: app.remoteType,
640
+ versionBoundary: {
641
+ packageName: buildIdentity.packageName,
642
+ version: buildIdentity.version,
643
+ buildVersion: buildIdentity.buildVersion,
644
+ unitId: buildIdentity.unitId,
645
+ sourceRevision: buildIdentity.sourceRevision,
646
+ },
647
+ smokeChecks,
648
+ status: 'pass',
649
+ };
650
+ }
651
+
652
+ function parseArgs(argv) {
653
+ const parsed = { app: undefined, out: defaultOut, target: 'dist' };
654
+
655
+ for (let index = 0; index < argv.length; index += 1) {
656
+ const arg = argv[index];
657
+ if (arg === '--out') {
658
+ parsed.out = argv[index + 1];
659
+ index += 1;
660
+ } else if (arg === '--app') {
661
+ parsed.app = argv[index + 1];
662
+ index += 1;
663
+ } else if (arg === '--target') {
664
+ parsed.target = argv[index + 1];
665
+ index += 1;
666
+ } else if (arg === '--help' || arg === '-h') {
667
+ parsed.help = true;
668
+ } else {
669
+ throw new Error(`Unknown argument: ${arg}`);
670
+ }
671
+ }
672
+
673
+ if (!parsed.out) {
674
+ throw new Error('--out requires a path');
675
+ }
676
+ if (!parsed.target) {
677
+ throw new Error('--target requires a directory name');
678
+ }
679
+
680
+ return parsed;
681
+ }
682
+
683
+ function printHelp() {
684
+ process.stdout
685
+ .write(`Usage: node scripts/proof-node-backend-federation.mjs [--app id] [--target dist] [--out proof.json]
686
+ `);
687
+ }
688
+
689
+ async function main(argv = process.argv.slice(2)) {
690
+ const args = parseArgs(argv);
691
+ if (args.help) {
692
+ printHelp();
693
+ return 0;
694
+ }
695
+
696
+ const config = readJson(compactConfigPath);
697
+ const apps = compactApps(config, args.app);
698
+ const results = [];
699
+ const backendRuntime =
700
+ apps.length > 0 ? await importBackendFederationRuntime() : undefined;
701
+
702
+ for (const app of apps) {
703
+ results.push(await proveBackend(app, backendRuntime, args.target));
704
+ }
705
+
706
+ const report = {
707
+ schemaVersion: 1,
708
+ generatedAt: new Date().toISOString(),
709
+ status: results.length > 0 ? 'pass' : 'skipped',
710
+ target: args.target,
711
+ results,
712
+ };
713
+
714
+ fs.mkdirSync(path.dirname(args.out), { recursive: true });
715
+ fs.writeFileSync(args.out, `${JSON.stringify(report, null, 2)}\n`);
716
+ process.stdout.write(`[node-backend-federation-proof] ${report.status}: ${args.out}\n`);
717
+
718
+ return 0;
719
+ }
720
+
721
+ main().then(
722
+ (exitCode) => {
723
+ process.exitCode = exitCode;
724
+ },
725
+ (error) => {
726
+ process.stderr.write(`[node-backend-federation-proof] ${error.message}\n`);
727
+ process.exitCode = 1;
728
+ },
729
+ );