@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.4 → 3.9.0-ultramodern.5

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/dist/cjs/backend-federation-build/codegen.js +256 -0
  2. package/dist/cjs/backend-federation-build/config.js +204 -0
  3. package/dist/cjs/backend-federation-build/emit.js +150 -0
  4. package/dist/cjs/backend-federation-build/index.js +53 -0
  5. package/dist/cjs/build-config/build-environment.js +268 -0
  6. package/dist/cjs/build-config/precompress/plugin.js +124 -0
  7. package/dist/cjs/build-config/precompress/types.js +18 -0
  8. package/dist/cjs/build-config/public.js +46 -0
  9. package/dist/cjs/cloudflare/constants.js +1 -1
  10. package/dist/cjs/cloudflare/delivery-unit.js +9 -9
  11. package/dist/cjs/cloudflare-output-verifier/identity.js +6 -6
  12. package/dist/cjs/config.js +29 -1
  13. package/dist/cjs/deploy-output/npmAliases.js +226 -0
  14. package/dist/cjs/deploy-output/plugin.js +76 -0
  15. package/dist/cjs/deploy-output/target.js +48 -0
  16. package/dist/cjs/native-type-checker.js +6 -3
  17. package/dist/cjs/release-envelope/framework-output.js +27 -27
  18. package/dist/cjs/release-envelope/plugin.js +2 -1
  19. package/dist/esm/backend-federation-build/codegen.mjs +205 -0
  20. package/dist/esm/backend-federation-build/config.mjs +128 -0
  21. package/dist/esm/backend-federation-build/emit.mjs +99 -0
  22. package/dist/esm/backend-federation-build/index.mjs +1 -0
  23. package/dist/esm/build-config/build-environment.mjs +224 -0
  24. package/dist/esm/build-config/precompress/plugin.mjs +76 -0
  25. package/dist/esm/build-config/precompress/types.mjs +0 -0
  26. package/dist/esm/build-config/public.mjs +1 -0
  27. package/dist/esm/cloudflare/constants.mjs +1 -1
  28. package/dist/esm/cloudflare/delivery-unit.mjs +1 -1
  29. package/dist/esm/cloudflare-output-verifier/identity.mjs +1 -1
  30. package/dist/esm/config.mjs +1 -0
  31. package/dist/esm/deploy-output/npmAliases.mjs +174 -0
  32. package/dist/esm/deploy-output/plugin.mjs +28 -0
  33. package/dist/esm/deploy-output/target.mjs +10 -0
  34. package/dist/esm/native-type-checker.mjs +6 -3
  35. package/dist/esm/release-envelope/framework-output.mjs +1 -1
  36. package/dist/esm/release-envelope/plugin.mjs +2 -1
  37. package/dist/esm-node/backend-federation-build/codegen.mjs +206 -0
  38. package/dist/esm-node/backend-federation-build/config.mjs +129 -0
  39. package/dist/esm-node/backend-federation-build/emit.mjs +100 -0
  40. package/dist/esm-node/backend-federation-build/index.mjs +2 -0
  41. package/dist/esm-node/build-config/build-environment.mjs +225 -0
  42. package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
  43. package/dist/esm-node/build-config/precompress/types.mjs +1 -0
  44. package/dist/esm-node/build-config/public.mjs +2 -0
  45. package/dist/esm-node/cloudflare/constants.mjs +1 -1
  46. package/dist/esm-node/cloudflare/delivery-unit.mjs +1 -1
  47. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +1 -1
  48. package/dist/esm-node/config.mjs +1 -0
  49. package/dist/esm-node/deploy-output/npmAliases.mjs +175 -0
  50. package/dist/esm-node/deploy-output/plugin.mjs +31 -0
  51. package/dist/esm-node/deploy-output/target.mjs +11 -0
  52. package/dist/esm-node/native-type-checker.mjs +6 -3
  53. package/dist/esm-node/release-envelope/framework-output.mjs +1 -1
  54. package/dist/esm-node/release-envelope/plugin.mjs +2 -1
  55. package/dist/types/backend-federation-build/codegen.d.ts +86 -0
  56. package/dist/types/backend-federation-build/config.d.ts +103 -0
  57. package/dist/types/backend-federation-build/emit.d.ts +21 -0
  58. package/dist/types/backend-federation-build/index.d.ts +2 -0
  59. package/dist/types/build-config/build-environment.d.ts +22 -0
  60. package/dist/types/build-config/precompress/plugin.d.ts +4 -0
  61. package/dist/types/build-config/precompress/types.d.ts +19 -0
  62. package/dist/types/build-config/public.d.ts +2 -0
  63. package/dist/types/cloudflare/constants.d.ts +1 -1
  64. package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
  65. package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
  66. package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
  67. package/dist/types/config.d.ts +2 -0
  68. package/dist/types/deploy-output/npmAliases.d.ts +14 -0
  69. package/dist/types/deploy-output/plugin.d.ts +15 -0
  70. package/dist/types/deploy-output/target.d.ts +6 -0
  71. package/dist/types/precompress/LICENSE +20 -0
  72. package/dist/types/precompress/index.d.ts +76 -0
  73. package/package.json +50 -8
  74. package/src/backend-federation-build/codegen.ts +245 -0
  75. package/src/backend-federation-build/config.ts +376 -0
  76. package/src/backend-federation-build/emit.ts +205 -0
  77. package/src/backend-federation-build/index.ts +2 -0
  78. package/src/build-config/build-environment.ts +404 -0
  79. package/src/build-config/precompress/plugin.ts +140 -0
  80. package/src/build-config/precompress/types.ts +26 -0
  81. package/src/build-config/public.ts +6 -0
  82. package/src/cloudflare/constants.ts +1 -1
  83. package/src/cloudflare/delivery-unit.ts +2 -2
  84. package/src/cloudflare/worker-manifest.ts +1 -0
  85. package/src/cloudflare-builder.ts +1 -0
  86. package/src/cloudflare-output-verifier/identity.ts +1 -1
  87. package/src/config.ts +7 -0
  88. package/src/deploy-output/npmAliases.ts +333 -0
  89. package/src/deploy-output/plugin.ts +49 -0
  90. package/src/deploy-output/target.ts +23 -0
  91. package/src/native-type-checker.ts +11 -3
  92. package/src/release-envelope/framework-output.ts +1 -1
  93. package/src/release-envelope/plugin.ts +5 -1
@@ -0,0 +1,76 @@
1
+ type BasePluginOptions<T> = {
2
+ /**
3
+ * include all assets that pass test assertion
4
+ */
5
+ test?: Rules | undefined;
6
+ /**
7
+ * include all assets matching any of these conditions
8
+ */
9
+ include?: Rules | undefined;
10
+ /**
11
+ * exclude all assets matching any of these conditions
12
+ */
13
+ exclude?: Rules | undefined;
14
+ /**
15
+ * only assets bigger than this size are processed, in bytes
16
+ */
17
+ threshold?: number | undefined;
18
+ /**
19
+ * only assets that compress better than this ratio are processed (`minRatio = Compressed Size / Original Size`)
20
+ */
21
+ minRatio?: number | undefined;
22
+ /**
23
+ * whether to delete the original assets or not
24
+ */
25
+ deleteOriginalAssets?: DeleteOriginalAssets | undefined;
26
+ /**
27
+ * the target asset filename
28
+ */
29
+ filename?: Filename | undefined;
30
+ };
31
+ type Rules = Rule[] | Rule;
32
+ type Rule = RegExp | string;
33
+ type DeleteOriginalAssets =
34
+ | boolean
35
+ | "keep-source-map"
36
+ | ((name: string) => boolean);
37
+ type Filename = string | ((fileData: PathData) => string);
38
+ type PathData = import('@rsbuild/core').Rspack.PathData;
39
+ type DefinedDefaultAlgorithmAndOptions<T> = T extends ZlibOptions
40
+ ? {
41
+ algorithm?: string | AlgorithmFunction<T> | undefined;
42
+ compressionOptions?: CompressionOptions<T> | undefined;
43
+ }
44
+ : {
45
+ algorithm: string | AlgorithmFunction<T>;
46
+ compressionOptions?: CompressionOptions<T> | undefined;
47
+ };
48
+ type ZlibOptions = import("zlib").ZlibOptions;
49
+ type AlgorithmFunction<T> = (
50
+ input: Buffer,
51
+ options: CompressionOptions<T>,
52
+ callback: (
53
+ error: Error | null | undefined,
54
+ result:
55
+ | WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>
56
+ | Uint8Array
57
+ | ReadonlyArray<number>
58
+ | WithImplicitCoercion<Uint8Array | ReadonlyArray<number> | string>
59
+ | WithImplicitCoercion<string>
60
+ | {
61
+ [Symbol.toPrimitive](hint: "string"): string;
62
+ },
63
+ ) => void,
64
+ ) => any;
65
+ type CompressionOptions<T> = InferDefaultType<T>;
66
+ type InferDefaultType<T> = T extends infer U ? U : CustomOptions;
67
+ type CustomOptions = {
68
+ [key: string]: EXPECTED_ANY;
69
+ };
70
+ type EXPECTED_ANY = any;
71
+ type WithImplicitCoercion<T> =
72
+ | T
73
+ | {
74
+ valueOf(): T;
75
+ };
76
+ export type CompressionPluginOptions = BasePluginOptions<ZlibOptions> & DefinedDefaultAlgorithmAndOptions<ZlibOptions>;
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/solutions/app-tools-extensions"
12
12
  },
13
13
  "license": "MIT",
14
- "version": "3.9.0-ultramodern.4",
14
+ "version": "3.9.0-ultramodern.5",
15
15
  "files": [
16
16
  "dist",
17
17
  "src"
@@ -86,6 +86,26 @@
86
86
  "types": "./dist/types/native-type-checker.d.ts",
87
87
  "import": "./dist/esm-node/native-type-checker.mjs",
88
88
  "require": "./dist/cjs/native-type-checker.js"
89
+ },
90
+ "./build-config/precompress/plugin": {
91
+ "types": "./dist/types/build-config/precompress/plugin.d.ts",
92
+ "import": "./dist/esm-node/build-config/precompress/plugin.mjs",
93
+ "require": "./dist/cjs/build-config/precompress/plugin.js"
94
+ },
95
+ "./backend-federation-build": {
96
+ "types": "./dist/types/backend-federation-build/index.d.ts",
97
+ "import": "./dist/esm-node/backend-federation-build/index.mjs",
98
+ "require": "./dist/cjs/backend-federation-build/index.js"
99
+ },
100
+ "./deploy-output/plugin": {
101
+ "types": "./dist/types/deploy-output/plugin.d.ts",
102
+ "import": "./dist/esm-node/deploy-output/plugin.mjs",
103
+ "require": "./dist/cjs/deploy-output/plugin.js"
104
+ },
105
+ "./deploy-output/target": {
106
+ "types": "./dist/types/deploy-output/target.d.ts",
107
+ "import": "./dist/esm-node/deploy-output/target.mjs",
108
+ "require": "./dist/cjs/deploy-output/target.js"
89
109
  }
90
110
  },
91
111
  "typesVersions": {
@@ -93,6 +113,18 @@
93
113
  "*": [
94
114
  "./dist/types/*",
95
115
  "./src/*"
116
+ ],
117
+ "build-config/precompress/plugin": [
118
+ "./dist/types/build-config/precompress/plugin.d.ts"
119
+ ],
120
+ "backend-federation-build": [
121
+ "./dist/types/backend-federation-build/index.d.ts"
122
+ ],
123
+ "deploy-output/plugin": [
124
+ "./dist/types/deploy-output/plugin.d.ts"
125
+ ],
126
+ "deploy-output/target": [
127
+ "./dist/types/deploy-output/target.d.ts"
96
128
  ]
97
129
  }
98
130
  },
@@ -103,17 +135,24 @@
103
135
  "@babel/parser": "^8.0.4",
104
136
  "@babel/traverse": "^8.0.4",
105
137
  "@loadable/component": "5.16.7",
106
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.4",
107
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.4",
108
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.4",
109
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.4",
138
+ "@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.5",
139
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.5",
140
+ "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.5",
141
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.5",
142
+ "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.5",
143
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.5",
144
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.5",
110
145
  "@swc/helpers": "^0.5.23",
146
+ "compression-webpack-plugin": "^12.0.0",
147
+ "esbuild": "^0.28.2",
111
148
  "std-env": "4.2.0"
112
149
  },
113
150
  "peerDependencies": {
114
151
  "@rsbuild/core": "2.2.3"
115
152
  },
116
153
  "devDependencies": {
154
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.5",
155
+ "@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.5",
117
156
  "@module-federation/runtime-tools": "2.9.0",
118
157
  "@rsbuild/core": "2.2.3",
119
158
  "@rslib/core": "1.0.0",
@@ -121,7 +160,9 @@
121
160
  "@types/babel__traverse": "7.28.0",
122
161
  "@types/node": "^26.4.1",
123
162
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
124
- "core-js": "^3.50.0"
163
+ "core-js": "^3.50.0",
164
+ "effect": "4.0.0-rc.112",
165
+ "ndepe": "^0.1.13"
125
166
  },
126
167
  "sideEffects": false,
127
168
  "publishConfig": {
@@ -130,7 +171,8 @@
130
171
  "scripts": {
131
172
  "pretest": "pnpm run build",
132
173
  "dev": "rslib build --watch",
133
- "build": "rslib build",
134
- "test": "rstest"
174
+ "build": "rslib build && pnpm run verify:public-surface",
175
+ "test": "rstest",
176
+ "verify:public-surface": "node ./tests/build-config/verify-public-surface.mjs"
135
177
  }
136
178
  }
@@ -0,0 +1,245 @@
1
+ import path from 'node:path';
2
+ import {
3
+ BACKEND_FEDERATION_EFFECT_EXPOSE as BACKEND_EXPOSE,
4
+ BACKEND_FEDERATION_MANIFEST_FILE as BACKEND_MANIFEST_FILE,
5
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE as BACKEND_REMOTE_ENTRY_FILE,
6
+ BACKEND_FEDERATION_CONTRACT_VERSION as CONTRACT_VERSION,
7
+ formatBackendFederationValidationErrors,
8
+ BACKEND_FEDERATION_NODE_ADAPTER_VERSION as NODE_ADAPTER_VERSION,
9
+ validateBackendFederationManifest,
10
+ } from '@modern-js/backend-federation-contracts';
11
+ import type { BackendFederationEntryIntegrity } from '@modern-js/server-runtime-extensions/backend-federation-security';
12
+ import { build } from 'esbuild';
13
+ import { type BackendFederationApp, normalizeRelativePath } from './config';
14
+
15
+ const createRelativeImportSpecifier = (
16
+ fromDirectory: string,
17
+ toFile: string,
18
+ ) => {
19
+ const relativePath = normalizeRelativePath(
20
+ path.relative(fromDirectory, toFile),
21
+ );
22
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
23
+ };
24
+
25
+ const createPublicPath = (containerEntry: string, fallbackPort: number) => {
26
+ try {
27
+ return new URL('.', containerEntry).href;
28
+ } catch {
29
+ const slashIndex = containerEntry.lastIndexOf('/');
30
+ if (slashIndex >= 0) {
31
+ return containerEntry.slice(0, slashIndex + 1);
32
+ }
33
+
34
+ return `http://localhost:${fallbackPort}/`;
35
+ }
36
+ };
37
+
38
+ export const createBackendRemoteEntrySource = async (
39
+ workspaceRoot: string,
40
+ app: BackendFederationApp,
41
+ effectApiPath: string,
42
+ entryPath: string,
43
+ ) => {
44
+ const effectApiSpecifier = createRelativeImportSpecifier(
45
+ path.dirname(effectApiPath),
46
+ effectApiPath,
47
+ );
48
+ const containerMetadata = {
49
+ buildVersion: app.buildVersion,
50
+ expose: BACKEND_EXPOSE,
51
+ name: app.backendName,
52
+ packageName: app.packageName,
53
+ remoteType: app.remoteType,
54
+ source: normalizeRelativePath(path.relative(workspaceRoot, effectApiPath)),
55
+ version: app.version,
56
+ ...(app.unitId ? { unitId: app.unitId } : {}),
57
+ ...(app.sourceRevision ? { sourceRevision: app.sourceRevision } : {}),
58
+ };
59
+ const compatibility = {
60
+ build: app.buildVersion,
61
+ contractVersion: CONTRACT_VERSION,
62
+ nodeAdapterVersion: NODE_ADAPTER_VERSION,
63
+ packageName: app.packageName,
64
+ ...(app.unitId ? { unitId: app.unitId } : {}),
65
+ ...(app.sourceRevision ? { sourceRevision: app.sourceRevision } : {}),
66
+ };
67
+ const source = `// Generated by Modern.js backend federation build.
68
+ import * as exposedNamespace from ${JSON.stringify(effectApiSpecifier)};
69
+
70
+ const exposedModule = {
71
+ ...exposedNamespace,
72
+ backendFederationContract: {
73
+ ...(exposedNamespace.backendFederationContract ?? {}),
74
+ compatibility: {
75
+ ...(exposedNamespace.backendFederationContract?.compatibility ?? {}),
76
+ ...${JSON.stringify(compatibility)},
77
+ },
78
+ name: ${JSON.stringify(app.backendName)},
79
+ role: 'microvertical-server',
80
+ runtimeFramework: 'effect',
81
+ strictEffectApproach: true,
82
+ },
83
+ };
84
+ const exposedModules = {
85
+ ${JSON.stringify(BACKEND_EXPOSE)}: () => Promise.resolve(exposedModule),
86
+ };
87
+ const __modernjsBackendFederation = ${JSON.stringify(containerMetadata)};
88
+
89
+ function init() {}
90
+
91
+ function get(id) {
92
+ const load = exposedModules[id];
93
+ if (!load) {
94
+ throw new Error(\`Unexpected backend federation expose: \${id}\`);
95
+ }
96
+
97
+ return async () => load();
98
+ }
99
+
100
+ module.exports = { __modernjsBackendFederation, get, init };
101
+ `;
102
+ const result = await build({
103
+ absWorkingDir: path.dirname(effectApiPath),
104
+ bundle: true,
105
+ format: 'cjs',
106
+ logLevel: 'silent',
107
+ platform: 'node',
108
+ stdin: {
109
+ contents: source,
110
+ loader: 'ts',
111
+ resolveDir: path.dirname(effectApiPath),
112
+ sourcefile: path.basename(entryPath),
113
+ },
114
+ target: 'node20',
115
+ write: false,
116
+ });
117
+ const output = result.outputFiles[0];
118
+ if (!output) {
119
+ throw new Error(
120
+ '[backend-federation-build] CommonJS backend federation container emitted no output.',
121
+ );
122
+ }
123
+ return output.text;
124
+ };
125
+
126
+ export const createBackendManifest = (
127
+ workspaceRoot: string,
128
+ distDirectory: string,
129
+ app: BackendFederationApp,
130
+ entryIntegrity: BackendFederationEntryIntegrity,
131
+ ) => {
132
+ const sourceModule = `${app.directory}/api/effect-api.ts`;
133
+ const publicPath = createPublicPath(app.containerEntry, app.port);
134
+
135
+ const manifest = {
136
+ schemaVersion: 1,
137
+ id: app.backendName,
138
+ name: app.backendName,
139
+ version: app.version,
140
+ buildVersion: app.buildVersion,
141
+ metaData: {
142
+ name: app.backendName,
143
+ type: 'backend',
144
+ buildInfo: {
145
+ buildVersion: app.buildVersion,
146
+ buildName: app.packageName,
147
+ },
148
+ remoteEntry: {
149
+ name: BACKEND_REMOTE_ENTRY_FILE,
150
+ path: '',
151
+ type: app.remoteType,
152
+ },
153
+ globalName: app.backendName,
154
+ publicPath,
155
+ ssrRemoteEntry: {
156
+ name: BACKEND_REMOTE_ENTRY_FILE,
157
+ path: '',
158
+ type: app.remoteType,
159
+ },
160
+ ssrPublicPath: publicPath,
161
+ },
162
+ entry: {
163
+ ...entryIntegrity,
164
+ file: BACKEND_REMOTE_ENTRY_FILE,
165
+ path: normalizeRelativePath(
166
+ path.relative(
167
+ workspaceRoot,
168
+ path.join(distDirectory, BACKEND_REMOTE_ENTRY_FILE),
169
+ ),
170
+ ),
171
+ type: app.remoteType,
172
+ url: app.containerEntry,
173
+ },
174
+ exposes: [
175
+ {
176
+ id: `${app.backendName}:${BACKEND_EXPOSE}`,
177
+ name: BACKEND_EXPOSE,
178
+ path: '',
179
+ assets: {
180
+ js: {
181
+ async: [],
182
+ sync: [BACKEND_REMOTE_ENTRY_FILE],
183
+ },
184
+ css: {
185
+ async: [],
186
+ sync: [],
187
+ },
188
+ },
189
+ },
190
+ ],
191
+ shared: [],
192
+ backendFederation: {
193
+ role: 'microvertical-server',
194
+ appId: app.id,
195
+ name: app.backendName,
196
+ runtimeFramework: 'effect',
197
+ strictEffectApproach: true,
198
+ contractVersion: CONTRACT_VERSION,
199
+ nodeAdapterVersion: NODE_ADAPTER_VERSION,
200
+ remoteType: app.remoteType,
201
+ expose: BACKEND_EXPOSE,
202
+ manifestUrl: app.manifestUrl,
203
+ containerEntry: app.containerEntry,
204
+ source: {
205
+ module: sourceModule,
206
+ },
207
+ readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
208
+ openapiPath: `${app.apiPrefix}/openapi.json`,
209
+ ...(app.deliveryUnit ? { deliveryUnit: app.deliveryUnit } : {}),
210
+ versionBoundary: {
211
+ invariant: 'web-and-api-same-build',
212
+ packageName: app.packageName,
213
+ version: app.version,
214
+ buildVersion: app.buildVersion,
215
+ ...(app.uiManifestUrl ? { uiManifestUrl: app.uiManifestUrl } : {}),
216
+ ...(app.deliveryUnit
217
+ ? {
218
+ deliveryUnit: {
219
+ unitId: app.deliveryUnit.unitId,
220
+ buildMarker: app.deliveryUnit.buildMarker,
221
+ sourceRevision: app.deliveryUnit.sourceRevision,
222
+ },
223
+ }
224
+ : {}),
225
+ },
226
+ },
227
+ };
228
+
229
+ const validation = validateBackendFederationManifest(manifest, {
230
+ path: 'backendFederationManifest',
231
+ requireEffectExpose: true,
232
+ requireEffectRuntime: true,
233
+ requireVersionFields: true,
234
+ });
235
+
236
+ if (!validation.ok) {
237
+ throw new Error(
238
+ `[backend-federation-build] Invalid backend federation manifest: ${formatBackendFederationValidationErrors(
239
+ validation.errors,
240
+ )}.`,
241
+ );
242
+ }
243
+
244
+ return manifest;
245
+ };