@bleedingdev/modern-js-plugin-bff-extensions 3.8.3-ultramodern.2
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.
- package/LICENSE +22 -0
- package/README.md +29 -0
- package/dist/cjs/backend-federation/edge-runtime.js +230 -0
- package/dist/cjs/backend-federation/edge.js +80 -0
- package/dist/cjs/backend-federation/entry.js +94 -0
- package/dist/cjs/backend-federation/identity.js +82 -0
- package/dist/cjs/backend-federation/index.js +61 -0
- package/dist/cjs/backend-federation/legacy-warning.js +40 -0
- package/dist/cjs/backend-federation/load.js +72 -0
- package/dist/cjs/backend-federation/node.js +50 -0
- package/dist/cjs/backend-federation/plugin.js +63 -0
- package/dist/cjs/backend-federation/remotes.js +51 -0
- package/dist/cjs/backend-federation/runtime.js +128 -0
- package/dist/cjs/backend-federation/types.js +18 -0
- package/dist/cjs/backend-federation/utils.js +48 -0
- package/dist/cjs/backend-federation/validate-loaded.js +56 -0
- package/dist/cjs/backend-federation-manifest/errors.js +83 -0
- package/dist/cjs/backend-federation-manifest/index.js +48 -0
- package/dist/cjs/backend-federation-manifest/load.js +173 -0
- package/dist/cjs/backend-federation-manifest/metadata.js +101 -0
- package/dist/cjs/backend-federation-manifest/node.js +50 -0
- package/dist/cjs/backend-federation-manifest/reference.js +132 -0
- package/dist/cjs/backend-federation-manifest/remote.js +81 -0
- package/dist/cjs/backend-federation-manifest/types.js +18 -0
- package/dist/cjs/backend-federation-manifest/validation.js +110 -0
- package/dist/cjs/client-generator/built-entry.js +84 -0
- package/dist/cjs/client-generator/generator.js +73 -0
- package/dist/cjs/client-generator/index.js +54 -0
- package/dist/cjs/client-generator/paths.js +62 -0
- package/dist/cjs/client-generator/rendering.js +253 -0
- package/dist/cjs/client-generator/runtime.js +68 -0
- package/dist/cjs/client-generator/types.js +18 -0
- package/dist/cjs/cross-project-generation/index.js +40 -0
- package/dist/cjs/cross-project-generation/render.js +53 -0
- package/dist/cjs/cross-project-policy/evaluation.js +75 -0
- package/dist/cjs/cross-project-policy/index.js +40 -0
- package/dist/cjs/cross-project-policy/node.js +76 -0
- package/dist/cjs/effect-adapter/cross-project-policy.js +64 -0
- package/dist/cjs/effect-adapter/entry.js +98 -0
- package/dist/cjs/effect-adapter/error-response.js +75 -0
- package/dist/cjs/effect-adapter/index.js +187 -0
- package/dist/cjs/effect-source-loader/index.js +51 -0
- package/dist/cjs/effect-source-loader/loader.js +341 -0
- package/dist/cjs/effect-source-loader/worker-runtime-wrapper.js +97 -0
- package/dist/cjs/hono/bind-route-handlers.js +76 -0
- package/dist/cjs/hono/cross-project-policy.js +48 -0
- package/dist/cjs/hono/index.js +40 -0
- package/dist/esm/backend-federation/edge-runtime.mjs +186 -0
- package/dist/esm/backend-federation/edge.mjs +21 -0
- package/dist/esm/backend-federation/entry.mjs +56 -0
- package/dist/esm/backend-federation/identity.mjs +41 -0
- package/dist/esm/backend-federation/index.mjs +4 -0
- package/dist/esm/backend-federation/legacy-warning.mjs +2 -0
- package/dist/esm/backend-federation/load.mjs +34 -0
- package/dist/esm/backend-federation/node.mjs +12 -0
- package/dist/esm/backend-federation/plugin.mjs +25 -0
- package/dist/esm/backend-federation/remotes.mjs +13 -0
- package/dist/esm/backend-federation/runtime.mjs +90 -0
- package/dist/esm/backend-federation/types.mjs +0 -0
- package/dist/esm/backend-federation/utils.mjs +7 -0
- package/dist/esm/backend-federation/validate-loaded.mjs +18 -0
- package/dist/esm/backend-federation-manifest/errors.mjs +32 -0
- package/dist/esm/backend-federation-manifest/index.mjs +3 -0
- package/dist/esm/backend-federation-manifest/load.mjs +135 -0
- package/dist/esm/backend-federation-manifest/metadata.mjs +42 -0
- package/dist/esm/backend-federation-manifest/node.mjs +12 -0
- package/dist/esm/backend-federation-manifest/reference.mjs +88 -0
- package/dist/esm/backend-federation-manifest/remote.mjs +43 -0
- package/dist/esm/backend-federation-manifest/types.mjs +0 -0
- package/dist/esm/backend-federation-manifest/validation.mjs +66 -0
- package/dist/esm/client-generator/built-entry.mjs +40 -0
- package/dist/esm/client-generator/generator.mjs +32 -0
- package/dist/esm/client-generator/index.mjs +3 -0
- package/dist/esm/client-generator/paths.mjs +24 -0
- package/dist/esm/client-generator/rendering.mjs +209 -0
- package/dist/esm/client-generator/runtime.mjs +27 -0
- package/dist/esm/client-generator/types.mjs +0 -0
- package/dist/esm/cross-project-generation/index.mjs +1 -0
- package/dist/esm/cross-project-generation/render.mjs +15 -0
- package/dist/esm/cross-project-policy/evaluation.mjs +34 -0
- package/dist/esm/cross-project-policy/index.mjs +1 -0
- package/dist/esm/cross-project-policy/node.mjs +28 -0
- package/dist/esm/effect-adapter/cross-project-policy.mjs +26 -0
- package/dist/esm/effect-adapter/entry.mjs +50 -0
- package/dist/esm/effect-adapter/error-response.mjs +37 -0
- package/dist/esm/effect-adapter/index.mjs +139 -0
- package/dist/esm/effect-source-loader/index.mjs +3 -0
- package/dist/esm/effect-source-loader/loader.mjs +285 -0
- package/dist/esm/effect-source-loader/worker-runtime-wrapper.mjs +56 -0
- package/dist/esm/hono/bind-route-handlers.mjs +38 -0
- package/dist/esm/hono/cross-project-policy.mjs +10 -0
- package/dist/esm/hono/index.mjs +1 -0
- package/dist/esm-node/backend-federation/edge-runtime.mjs +187 -0
- package/dist/esm-node/backend-federation/edge.mjs +22 -0
- package/dist/esm-node/backend-federation/entry.mjs +57 -0
- package/dist/esm-node/backend-federation/identity.mjs +42 -0
- package/dist/esm-node/backend-federation/index.mjs +5 -0
- package/dist/esm-node/backend-federation/legacy-warning.mjs +3 -0
- package/dist/esm-node/backend-federation/load.mjs +35 -0
- package/dist/esm-node/backend-federation/node.mjs +13 -0
- package/dist/esm-node/backend-federation/plugin.mjs +26 -0
- package/dist/esm-node/backend-federation/remotes.mjs +14 -0
- package/dist/esm-node/backend-federation/runtime.mjs +91 -0
- package/dist/esm-node/backend-federation/types.mjs +1 -0
- package/dist/esm-node/backend-federation/utils.mjs +8 -0
- package/dist/esm-node/backend-federation/validate-loaded.mjs +19 -0
- package/dist/esm-node/backend-federation-manifest/errors.mjs +33 -0
- package/dist/esm-node/backend-federation-manifest/index.mjs +4 -0
- package/dist/esm-node/backend-federation-manifest/load.mjs +136 -0
- package/dist/esm-node/backend-federation-manifest/metadata.mjs +43 -0
- package/dist/esm-node/backend-federation-manifest/node.mjs +13 -0
- package/dist/esm-node/backend-federation-manifest/reference.mjs +89 -0
- package/dist/esm-node/backend-federation-manifest/remote.mjs +44 -0
- package/dist/esm-node/backend-federation-manifest/types.mjs +1 -0
- package/dist/esm-node/backend-federation-manifest/validation.mjs +67 -0
- package/dist/esm-node/client-generator/built-entry.mjs +41 -0
- package/dist/esm-node/client-generator/generator.mjs +33 -0
- package/dist/esm-node/client-generator/index.mjs +4 -0
- package/dist/esm-node/client-generator/paths.mjs +25 -0
- package/dist/esm-node/client-generator/rendering.mjs +210 -0
- package/dist/esm-node/client-generator/runtime.mjs +29 -0
- package/dist/esm-node/client-generator/types.mjs +1 -0
- package/dist/esm-node/cross-project-generation/index.mjs +2 -0
- package/dist/esm-node/cross-project-generation/render.mjs +16 -0
- package/dist/esm-node/cross-project-policy/evaluation.mjs +35 -0
- package/dist/esm-node/cross-project-policy/index.mjs +2 -0
- package/dist/esm-node/cross-project-policy/node.mjs +29 -0
- package/dist/esm-node/effect-adapter/cross-project-policy.mjs +27 -0
- package/dist/esm-node/effect-adapter/entry.mjs +51 -0
- package/dist/esm-node/effect-adapter/error-response.mjs +38 -0
- package/dist/esm-node/effect-adapter/index.mjs +140 -0
- package/dist/esm-node/effect-source-loader/index.mjs +4 -0
- package/dist/esm-node/effect-source-loader/loader.mjs +286 -0
- package/dist/esm-node/effect-source-loader/worker-runtime-wrapper.mjs +57 -0
- package/dist/esm-node/hono/bind-route-handlers.mjs +39 -0
- package/dist/esm-node/hono/cross-project-policy.mjs +11 -0
- package/dist/esm-node/hono/index.mjs +2 -0
- package/dist/types/backend-federation/edge-runtime.d.ts +46 -0
- package/dist/types/backend-federation/edge.d.ts +28 -0
- package/dist/types/backend-federation/entry.d.ts +2 -0
- package/dist/types/backend-federation/identity.d.ts +30 -0
- package/dist/types/backend-federation/index.d.ts +6 -0
- package/dist/types/backend-federation/legacy-warning.d.ts +1 -0
- package/dist/types/backend-federation/load.d.ts +13 -0
- package/dist/types/backend-federation/node.d.ts +4 -0
- package/dist/types/backend-federation/plugin.d.ts +3 -0
- package/dist/types/backend-federation/remotes.d.ts +2 -0
- package/dist/types/backend-federation/runtime.d.ts +3 -0
- package/dist/types/backend-federation/types.d.ts +76 -0
- package/dist/types/backend-federation/utils.d.ts +2 -0
- package/dist/types/backend-federation/validate-loaded.d.ts +8 -0
- package/dist/types/backend-federation-manifest/errors.d.ts +11 -0
- package/dist/types/backend-federation-manifest/index.d.ts +5 -0
- package/dist/types/backend-federation-manifest/load.d.ts +3 -0
- package/dist/types/backend-federation-manifest/metadata.d.ts +18 -0
- package/dist/types/backend-federation-manifest/node.d.ts +3 -0
- package/dist/types/backend-federation-manifest/reference.d.ts +4 -0
- package/dist/types/backend-federation-manifest/remote.d.ts +3 -0
- package/dist/types/backend-federation-manifest/types.d.ts +49 -0
- package/dist/types/backend-federation-manifest/validation.d.ts +5 -0
- package/dist/types/client-generator/built-entry.d.ts +16 -0
- package/dist/types/client-generator/generator.d.ts +3 -0
- package/dist/types/client-generator/index.d.ts +4 -0
- package/dist/types/client-generator/paths.d.ts +5 -0
- package/dist/types/client-generator/rendering.d.ts +24 -0
- package/dist/types/client-generator/runtime.d.ts +12 -0
- package/dist/types/client-generator/types.d.ts +33 -0
- package/dist/types/cross-project-generation/index.d.ts +1 -0
- package/dist/types/cross-project-generation/render.d.ts +1 -0
- package/dist/types/cross-project-policy/evaluation.d.ts +4 -0
- package/dist/types/cross-project-policy/index.d.ts +1 -0
- package/dist/types/cross-project-policy/node.d.ts +4 -0
- package/dist/types/effect-adapter/cross-project-policy.d.ts +4 -0
- package/dist/types/effect-adapter/entry.d.ts +2 -0
- package/dist/types/effect-adapter/error-response.d.ts +2 -0
- package/dist/types/effect-adapter/index.d.ts +19 -0
- package/dist/types/effect-source-loader/index.d.ts +3 -0
- package/dist/types/effect-source-loader/loader.d.ts +17 -0
- package/dist/types/effect-source-loader/worker-runtime-wrapper.d.ts +25 -0
- package/dist/types/hono/bind-route-handlers.d.ts +10 -0
- package/dist/types/hono/cross-project-policy.d.ts +3 -0
- package/dist/types/hono/index.d.ts +1 -0
- package/package.json +213 -0
- package/src/backend-federation/edge-runtime.ts +258 -0
- package/src/backend-federation/edge.ts +128 -0
- package/src/backend-federation/entry.ts +125 -0
- package/src/backend-federation/identity.ts +97 -0
- package/src/backend-federation/index.ts +30 -0
- package/src/backend-federation/legacy-warning.ts +2 -0
- package/src/backend-federation/load.ts +102 -0
- package/src/backend-federation/node.ts +33 -0
- package/src/backend-federation/plugin.ts +35 -0
- package/src/backend-federation/remotes.ts +17 -0
- package/src/backend-federation/runtime.ts +99 -0
- package/src/backend-federation/types.ts +103 -0
- package/src/backend-federation/utils.ts +7 -0
- package/src/backend-federation/validate-loaded.ts +72 -0
- package/src/backend-federation-manifest/errors.ts +85 -0
- package/src/backend-federation-manifest/index.ts +11 -0
- package/src/backend-federation-manifest/load.ts +266 -0
- package/src/backend-federation-manifest/metadata.ts +73 -0
- package/src/backend-federation-manifest/node.ts +19 -0
- package/src/backend-federation-manifest/reference.ts +240 -0
- package/src/backend-federation-manifest/remote.ts +102 -0
- package/src/backend-federation-manifest/types.ts +73 -0
- package/src/backend-federation-manifest/validation.ts +255 -0
- package/src/client-generator/built-entry.ts +70 -0
- package/src/client-generator/generator.ts +52 -0
- package/src/client-generator/index.ts +10 -0
- package/src/client-generator/paths.ts +40 -0
- package/src/client-generator/rendering.ts +307 -0
- package/src/client-generator/runtime.ts +69 -0
- package/src/client-generator/types.ts +35 -0
- package/src/cross-project-generation/index.ts +1 -0
- package/src/cross-project-generation/render.ts +14 -0
- package/src/cross-project-policy/evaluation.ts +70 -0
- package/src/cross-project-policy/index.ts +1 -0
- package/src/cross-project-policy/node.ts +63 -0
- package/src/effect-adapter/cross-project-policy.ts +63 -0
- package/src/effect-adapter/entry.ts +95 -0
- package/src/effect-adapter/error-response.ts +68 -0
- package/src/effect-adapter/index.ts +246 -0
- package/src/effect-source-loader/index.ts +6 -0
- package/src/effect-source-loader/loader.ts +397 -0
- package/src/effect-source-loader/worker-runtime-wrapper.ts +90 -0
- package/src/hono/bind-route-handlers.ts +62 -0
- package/src/hono/cross-project-policy.ts +26 -0
- package/src/hono/index.ts +4 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off extendsNativeError:off globalTimers:off newPromise:off strictBooleanExpressions:off
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BackendFederationRemoteEntryError,
|
|
5
|
+
loadBoundedBackendFederationResource,
|
|
6
|
+
redactBackendFederationUrl,
|
|
7
|
+
} from '@modern-js/server-runtime-extensions/backend-federation-security';
|
|
8
|
+
import { BACKEND_FEDERATION_MANIFEST_FILE } from '../backend-federation';
|
|
9
|
+
import {
|
|
10
|
+
assertManifestAdapter,
|
|
11
|
+
BackendFederationManifestAdapterError,
|
|
12
|
+
} from './errors';
|
|
13
|
+
import { isRecord } from './metadata';
|
|
14
|
+
import type {
|
|
15
|
+
BackendFederationManifest,
|
|
16
|
+
BackendFederationManifestAdapterOptions,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
function getProcessEnv() {
|
|
20
|
+
return typeof process !== 'undefined' ? process.env : undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ManifestReferenceSource = 'env' | 'path' | 'url';
|
|
24
|
+
|
|
25
|
+
function resolveManifestReferenceSource(
|
|
26
|
+
options: Pick<
|
|
27
|
+
BackendFederationManifestAdapterOptions,
|
|
28
|
+
'manifestEnv' | 'manifestPath' | 'manifestUrl'
|
|
29
|
+
>,
|
|
30
|
+
): ManifestReferenceSource | undefined {
|
|
31
|
+
if (options.manifestPath) {
|
|
32
|
+
return 'path';
|
|
33
|
+
}
|
|
34
|
+
if (options.manifestUrl) {
|
|
35
|
+
return 'url';
|
|
36
|
+
}
|
|
37
|
+
if (options.manifestEnv) {
|
|
38
|
+
return 'env';
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resolveBackendFederationManifestReference(
|
|
44
|
+
options: Pick<
|
|
45
|
+
BackendFederationManifestAdapterOptions,
|
|
46
|
+
'env' | 'manifestEnv' | 'manifestPath' | 'manifestUrl'
|
|
47
|
+
>,
|
|
48
|
+
) {
|
|
49
|
+
if (options.manifestPath) {
|
|
50
|
+
return options.manifestPath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.manifestUrl) {
|
|
54
|
+
return options.manifestUrl;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!options.manifestEnv) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
options.env?.[options.manifestEnv] ?? getProcessEnv()?.[options.manifestEnv]
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function assertExpectedIdentityForReference(
|
|
67
|
+
options: Pick<BackendFederationManifestAdapterOptions, 'expected'>,
|
|
68
|
+
source: ManifestReferenceSource | undefined,
|
|
69
|
+
) {
|
|
70
|
+
if (source === undefined || source === 'path') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (options.expected?.unitId && options.expected.buildMarker) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw new BackendFederationManifestAdapterError(
|
|
79
|
+
'version_mismatch',
|
|
80
|
+
`[BFF][Effect] Backend federation ${source} manifest references require expected.unitId and expected.buildMarker.`,
|
|
81
|
+
undefined,
|
|
82
|
+
{
|
|
83
|
+
label: 'expected.deliveryUnit',
|
|
84
|
+
expected: 'unitId + buildMarker',
|
|
85
|
+
received: options.expected,
|
|
86
|
+
referenceSource: source,
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isHttpUrl(value: string) {
|
|
92
|
+
return /^https?:\/\//iu.test(value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function referenceForDiagnostics(reference: string) {
|
|
96
|
+
return isHttpUrl(reference)
|
|
97
|
+
? redactBackendFederationUrl(reference)
|
|
98
|
+
: '[local manifest]';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function readFileReference(reference: string) {
|
|
102
|
+
const [{ fileURLToPath }, fs] = await Promise.all([
|
|
103
|
+
import('node:url'),
|
|
104
|
+
import('node:fs/promises'),
|
|
105
|
+
]);
|
|
106
|
+
const filePath = reference.startsWith('file:')
|
|
107
|
+
? fileURLToPath(reference)
|
|
108
|
+
: reference;
|
|
109
|
+
|
|
110
|
+
return fs.readFile(filePath, 'utf8');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export async function withTimeout<T>(
|
|
114
|
+
promise: Promise<T>,
|
|
115
|
+
timeoutMs: number | undefined,
|
|
116
|
+
label: string,
|
|
117
|
+
) {
|
|
118
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
119
|
+
return promise;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
return await Promise.race([
|
|
126
|
+
promise,
|
|
127
|
+
new Promise<T>((_, reject) => {
|
|
128
|
+
timeout = setTimeout(() => {
|
|
129
|
+
reject(
|
|
130
|
+
new BackendFederationManifestAdapterError(
|
|
131
|
+
'timeout',
|
|
132
|
+
`[BFF][Effect] ${label} timed out after ${timeoutMs}ms.`,
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}, timeoutMs);
|
|
136
|
+
}),
|
|
137
|
+
]);
|
|
138
|
+
} finally {
|
|
139
|
+
if (timeout) {
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function loadBackendFederationManifest(
|
|
146
|
+
options: Pick<
|
|
147
|
+
BackendFederationManifestAdapterOptions,
|
|
148
|
+
| 'env'
|
|
149
|
+
| 'expected'
|
|
150
|
+
| 'fetch'
|
|
151
|
+
| 'manifest'
|
|
152
|
+
| 'manifestEnv'
|
|
153
|
+
| 'manifestPolicy'
|
|
154
|
+
| 'manifestPath'
|
|
155
|
+
| 'manifestUrl'
|
|
156
|
+
| 'signal'
|
|
157
|
+
| 'timeoutMs'
|
|
158
|
+
>,
|
|
159
|
+
): Promise<BackendFederationManifest> {
|
|
160
|
+
if (options.manifest) {
|
|
161
|
+
return options.manifest;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const reference = resolveBackendFederationManifestReference(options);
|
|
165
|
+
if (!reference) {
|
|
166
|
+
throw new BackendFederationManifestAdapterError(
|
|
167
|
+
'manifest_unavailable',
|
|
168
|
+
`[BFF][Effect] Backend federation manifest reference is missing. Pass manifest, manifestPath, manifestUrl, or manifestEnv for ${BACKEND_FEDERATION_MANIFEST_FILE}.`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
assertExpectedIdentityForReference(
|
|
172
|
+
options,
|
|
173
|
+
resolveManifestReferenceSource(options),
|
|
174
|
+
);
|
|
175
|
+
const referenceSource = resolveManifestReferenceSource(options);
|
|
176
|
+
if (referenceSource === 'path' && isHttpUrl(reference)) {
|
|
177
|
+
throw new BackendFederationManifestAdapterError(
|
|
178
|
+
'manifest_unavailable',
|
|
179
|
+
'[BFF][Effect] Backend federation manifestPath must identify an explicit local file. Use manifestUrl for HTTP(S) manifests.',
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
(referenceSource === 'url' || referenceSource === 'env') &&
|
|
184
|
+
!isHttpUrl(reference)
|
|
185
|
+
) {
|
|
186
|
+
throw new BackendFederationManifestAdapterError(
|
|
187
|
+
'manifest_unavailable',
|
|
188
|
+
`[BFF][Effect] Backend federation ${referenceSource} manifest references must use HTTP(S). Use manifestPath for an explicit local file.`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const diagnosticReference = referenceForDiagnostics(reference);
|
|
194
|
+
const source = isHttpUrl(reference)
|
|
195
|
+
? new TextDecoder('utf-8', { fatal: true }).decode(
|
|
196
|
+
await loadBoundedBackendFederationResource(reference, {
|
|
197
|
+
allowInsecureHttp:
|
|
198
|
+
options.manifestPolicy?.allowInsecureHttp === true,
|
|
199
|
+
fetch: options.fetch,
|
|
200
|
+
kind: 'manifest',
|
|
201
|
+
maxBytes: options.manifestPolicy?.maxBytes,
|
|
202
|
+
signal: options.manifestPolicy?.signal ?? options.signal,
|
|
203
|
+
timeoutMs: options.manifestPolicy?.timeoutMs ?? options.timeoutMs,
|
|
204
|
+
}),
|
|
205
|
+
)
|
|
206
|
+
: await withTimeout(
|
|
207
|
+
readFileReference(reference),
|
|
208
|
+
options.manifestPolicy?.timeoutMs ?? options.timeoutMs,
|
|
209
|
+
'Backend federation local manifest',
|
|
210
|
+
);
|
|
211
|
+
const manifest = JSON.parse(source) as unknown;
|
|
212
|
+
|
|
213
|
+
assertManifestAdapter(
|
|
214
|
+
isRecord(manifest),
|
|
215
|
+
'manifest_invalid',
|
|
216
|
+
`[BFF][Effect] Backend federation manifest ${diagnosticReference} must be a JSON object.`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return manifest;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (error instanceof BackendFederationManifestAdapterError) {
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (error instanceof BackendFederationRemoteEntryError) {
|
|
226
|
+
throw new BackendFederationManifestAdapterError(
|
|
227
|
+
error.code === 'timeout' ? 'timeout' : 'manifest_unavailable',
|
|
228
|
+
`[BFF][Effect] ${error.message}`,
|
|
229
|
+
error,
|
|
230
|
+
error.details,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
throw new BackendFederationManifestAdapterError(
|
|
235
|
+
'manifest_unavailable',
|
|
236
|
+
`[BFF][Effect] Backend federation manifest ${referenceForDiagnostics(reference)} could not be loaded.`,
|
|
237
|
+
error,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off extendsNativeError:off globalTimers:off newPromise:off strictBooleanExpressions:off
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BackendFederationRemoteEntryError,
|
|
5
|
+
resolveBackendFederationRemoteEntryVerification,
|
|
6
|
+
} from '@modern-js/server-runtime-extensions/backend-federation-security';
|
|
7
|
+
import { backendFederationExposeNames } from '@modern-js/utils/universal';
|
|
8
|
+
import {
|
|
9
|
+
BACKEND_FEDERATION_EFFECT_EXPOSE,
|
|
10
|
+
type BackendFederationRemote,
|
|
11
|
+
} from '../backend-federation';
|
|
12
|
+
import {
|
|
13
|
+
assertManifestAdapter,
|
|
14
|
+
BackendFederationManifestAdapterError,
|
|
15
|
+
} from './errors';
|
|
16
|
+
import {
|
|
17
|
+
backendFederationMetadata,
|
|
18
|
+
recordField,
|
|
19
|
+
resolveRemoteEntryFromMetadata,
|
|
20
|
+
stringValue,
|
|
21
|
+
} from './metadata';
|
|
22
|
+
import type { BackendFederationManifest } from './types';
|
|
23
|
+
|
|
24
|
+
export function resolveBackendFederationRemoteFromManifest(
|
|
25
|
+
manifest: BackendFederationManifest,
|
|
26
|
+
remoteOverride: Partial<BackendFederationRemote> = {},
|
|
27
|
+
): BackendFederationRemote {
|
|
28
|
+
const backendFederation = backendFederationMetadata(manifest);
|
|
29
|
+
const entry = recordField(manifest, 'entry');
|
|
30
|
+
const metaData = recordField(manifest, 'metaData');
|
|
31
|
+
const remoteEntry = recordField(metaData, 'remoteEntry');
|
|
32
|
+
const remoteName =
|
|
33
|
+
remoteOverride.name ??
|
|
34
|
+
stringValue(backendFederation?.name) ??
|
|
35
|
+
stringValue(manifest.name) ??
|
|
36
|
+
stringValue(manifest.id);
|
|
37
|
+
const manifestEntryUrl =
|
|
38
|
+
stringValue(backendFederation?.containerEntry) ??
|
|
39
|
+
stringValue(entry?.url) ??
|
|
40
|
+
resolveRemoteEntryFromMetadata(manifest);
|
|
41
|
+
const remoteEntryUrl = remoteOverride.entry ?? manifestEntryUrl;
|
|
42
|
+
const remoteType =
|
|
43
|
+
remoteOverride.type ??
|
|
44
|
+
stringValue(backendFederation?.remoteType) ??
|
|
45
|
+
stringValue(entry?.type) ??
|
|
46
|
+
stringValue(remoteEntry?.type) ??
|
|
47
|
+
'module';
|
|
48
|
+
const expose =
|
|
49
|
+
remoteOverride.expose ??
|
|
50
|
+
backendFederationExposeNames(backendFederation)[0] ??
|
|
51
|
+
BACKEND_FEDERATION_EFFECT_EXPOSE;
|
|
52
|
+
let verification: BackendFederationRemote['verification'];
|
|
53
|
+
try {
|
|
54
|
+
const manifestVerification =
|
|
55
|
+
resolveBackendFederationRemoteEntryVerification(manifest);
|
|
56
|
+
verification =
|
|
57
|
+
remoteOverride.verification ??
|
|
58
|
+
(remoteEntryUrl === manifestEntryUrl ? manifestVerification : undefined);
|
|
59
|
+
} catch (cause) {
|
|
60
|
+
if (cause instanceof BackendFederationRemoteEntryError) {
|
|
61
|
+
throw new BackendFederationManifestAdapterError(
|
|
62
|
+
cause.code === 'identity_mismatch'
|
|
63
|
+
? 'version_mismatch'
|
|
64
|
+
: 'manifest_invalid',
|
|
65
|
+
`[BFF][Effect] ${cause.message}`,
|
|
66
|
+
cause,
|
|
67
|
+
cause.details,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
throw cause;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
assertManifestAdapter(
|
|
74
|
+
remoteName,
|
|
75
|
+
'manifest_invalid',
|
|
76
|
+
'[BFF][Effect] Backend federation manifest must declare a remote name.',
|
|
77
|
+
);
|
|
78
|
+
assertManifestAdapter(
|
|
79
|
+
remoteEntryUrl,
|
|
80
|
+
'manifest_invalid',
|
|
81
|
+
'[BFF][Effect] Backend federation manifest must declare a backend remote entry URL.',
|
|
82
|
+
);
|
|
83
|
+
assertManifestAdapter(
|
|
84
|
+
expose === BACKEND_FEDERATION_EFFECT_EXPOSE,
|
|
85
|
+
'manifest_invalid',
|
|
86
|
+
`[BFF][Effect] Backend federation manifest expose must be ${BACKEND_FEDERATION_EFFECT_EXPOSE}.`,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
name: remoteName,
|
|
91
|
+
entry: remoteEntryUrl,
|
|
92
|
+
type: remoteType,
|
|
93
|
+
...(verification ? { verification } : {}),
|
|
94
|
+
...(remoteOverride.entryGlobalName
|
|
95
|
+
? { entryGlobalName: remoteOverride.entryGlobalName }
|
|
96
|
+
: {}),
|
|
97
|
+
expose,
|
|
98
|
+
...(remoteOverride.shareScope
|
|
99
|
+
? { shareScope: remoteOverride.shareScope }
|
|
100
|
+
: {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off extendsNativeError:off globalTimers:off newPromise:off strictBooleanExpressions:off
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
BackendFederationRemoteEntryPolicy,
|
|
5
|
+
BackendFederationResourcePolicy,
|
|
6
|
+
BackendFederationResourceResponse,
|
|
7
|
+
} from '@modern-js/server-runtime-extensions/backend-federation-security';
|
|
8
|
+
import type {
|
|
9
|
+
ModuleFederation,
|
|
10
|
+
ModuleFederationRuntimePlugin,
|
|
11
|
+
} from '@module-federation/runtime';
|
|
12
|
+
import type {
|
|
13
|
+
BackendFederatedEffectApiModule,
|
|
14
|
+
BackendFederationRemote,
|
|
15
|
+
} from '../backend-federation';
|
|
16
|
+
import type { BackendFederationManifestAdapterError } from './errors';
|
|
17
|
+
|
|
18
|
+
export type BackendFederationManifest = Record<string, unknown>;
|
|
19
|
+
|
|
20
|
+
export type BackendFederationVersionBoundaryExpectation = {
|
|
21
|
+
buildVersion?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Delivery-unit build marker (ADR-0019). Semantically the same value as
|
|
24
|
+
* `buildVersion`; treated as an alias. If both are set and disagree the
|
|
25
|
+
* expectation itself is invalid and fails closed before any manifest
|
|
26
|
+
* comparison runs.
|
|
27
|
+
*/
|
|
28
|
+
buildMarker?: string;
|
|
29
|
+
contractVersion?: string;
|
|
30
|
+
nodeAdapterVersion?: string;
|
|
31
|
+
packageName?: string;
|
|
32
|
+
remoteName?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Delivery-unit identity root (ADR-0019 §3). When set, the manifest's
|
|
35
|
+
* `backendFederation.versionBoundary.deliveryUnit.unitId` (or the
|
|
36
|
+
* top-level `backendFederation.deliveryUnit.unitId` fallback) must match.
|
|
37
|
+
*/
|
|
38
|
+
unitId?: string;
|
|
39
|
+
version?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type BackendFederationManifestFetchResponse =
|
|
43
|
+
BackendFederationResourceResponse;
|
|
44
|
+
|
|
45
|
+
export type BackendFederationManifestAdapterFallback = (
|
|
46
|
+
error: BackendFederationManifestAdapterError,
|
|
47
|
+
context: {
|
|
48
|
+
manifest?: BackendFederationManifest;
|
|
49
|
+
remote?: BackendFederationRemote;
|
|
50
|
+
},
|
|
51
|
+
) => BackendFederatedEffectApiModule | Promise<BackendFederatedEffectApiModule>;
|
|
52
|
+
|
|
53
|
+
export type BackendFederationManifestAdapterOptions = {
|
|
54
|
+
entryPolicy?: BackendFederationRemoteEntryPolicy;
|
|
55
|
+
env?: Record<string, string | undefined>;
|
|
56
|
+
expected?: BackendFederationVersionBoundaryExpectation;
|
|
57
|
+
fallback?: BackendFederationManifestAdapterFallback;
|
|
58
|
+
fetch?: (
|
|
59
|
+
url: string,
|
|
60
|
+
init?: RequestInit,
|
|
61
|
+
) => Promise<BackendFederationManifestFetchResponse>;
|
|
62
|
+
hostName: string;
|
|
63
|
+
manifest?: BackendFederationManifest;
|
|
64
|
+
manifestEnv?: string;
|
|
65
|
+
manifestPolicy?: Omit<BackendFederationResourcePolicy, 'fetch'>;
|
|
66
|
+
manifestPath?: string;
|
|
67
|
+
manifestUrl?: string;
|
|
68
|
+
plugins?: ModuleFederationRuntimePlugin[];
|
|
69
|
+
remote?: Partial<BackendFederationRemote>;
|
|
70
|
+
runtime?: ModuleFederation;
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
timeoutMs?: number;
|
|
73
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// @effect-diagnostics asyncFunction:off extendsNativeError:off globalTimers:off newPromise:off strictBooleanExpressions:off
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatBackendFederationValidationErrors,
|
|
5
|
+
validateBackendFederationManifest as validateBackendFederationManifestContract,
|
|
6
|
+
} from '@modern-js/utils/universal';
|
|
7
|
+
import {
|
|
8
|
+
BACKEND_FEDERATION_CONTRACT_VERSION,
|
|
9
|
+
BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
|
|
10
|
+
type BackendFederatedEffectApiModule,
|
|
11
|
+
type BackendFederationRemote,
|
|
12
|
+
} from '../backend-federation';
|
|
13
|
+
import {
|
|
14
|
+
assertConsistentValue,
|
|
15
|
+
assertManifestAdapter,
|
|
16
|
+
assertVersionValue,
|
|
17
|
+
BackendFederationManifestAdapterError,
|
|
18
|
+
} from './errors';
|
|
19
|
+
import {
|
|
20
|
+
backendFederationMetadata,
|
|
21
|
+
manifestDeliveryUnit,
|
|
22
|
+
recordField,
|
|
23
|
+
stringValue,
|
|
24
|
+
versionBoundaryMetadata,
|
|
25
|
+
} from './metadata';
|
|
26
|
+
import type {
|
|
27
|
+
BackendFederationManifest,
|
|
28
|
+
BackendFederationVersionBoundaryExpectation,
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
function resolveExpectedBuildMarker(
|
|
32
|
+
expected: BackendFederationVersionBoundaryExpectation,
|
|
33
|
+
) {
|
|
34
|
+
if (
|
|
35
|
+
expected.buildVersion !== undefined &&
|
|
36
|
+
expected.buildMarker !== undefined &&
|
|
37
|
+
expected.buildVersion !== expected.buildMarker
|
|
38
|
+
) {
|
|
39
|
+
throw new BackendFederationManifestAdapterError(
|
|
40
|
+
'version_mismatch',
|
|
41
|
+
`[BFF][Effect] Backend federation expected buildVersion/buildMarker mismatch: buildVersion ${expected.buildVersion}, buildMarker ${expected.buildMarker}.`,
|
|
42
|
+
undefined,
|
|
43
|
+
{
|
|
44
|
+
label: 'expected.buildVersion/buildMarker',
|
|
45
|
+
expected: expected.buildVersion,
|
|
46
|
+
received: expected.buildMarker,
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Intentionally no fallback to `buildVersion` here: this is only used to
|
|
52
|
+
// compare against manifest `deliveryUnit.buildMarker`, which is a
|
|
53
|
+
// separate, additive field. Callers that only pass `buildVersion` (the
|
|
54
|
+
// pre-ADR-0019 expectation shape) must not be forced to also match a
|
|
55
|
+
// delivery-unit build marker on the manifest.
|
|
56
|
+
return expected.buildMarker;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function validateBackendFederationManifest(
|
|
60
|
+
manifest: BackendFederationManifest,
|
|
61
|
+
expected: BackendFederationVersionBoundaryExpectation = {},
|
|
62
|
+
) {
|
|
63
|
+
const expectedBuildMarker = resolveExpectedBuildMarker(expected);
|
|
64
|
+
const backendFederation = backendFederationMetadata(manifest);
|
|
65
|
+
assertManifestAdapter(
|
|
66
|
+
backendFederation,
|
|
67
|
+
'manifest_invalid',
|
|
68
|
+
'[BFF][Effect] Backend federation manifest must declare backendFederation metadata.',
|
|
69
|
+
);
|
|
70
|
+
const manifestShape = validateBackendFederationManifestContract(manifest, {
|
|
71
|
+
expectedContractVersion: false,
|
|
72
|
+
expectedNodeAdapterVersion: false,
|
|
73
|
+
validateDeliveryUnit: false,
|
|
74
|
+
});
|
|
75
|
+
assertManifestAdapter(
|
|
76
|
+
manifestShape.ok,
|
|
77
|
+
'manifest_invalid',
|
|
78
|
+
`[BFF][Effect] Backend federation manifest schema invalid: ${formatBackendFederationValidationErrors(
|
|
79
|
+
manifestShape.errors,
|
|
80
|
+
)}.`,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
assertManifestAdapter(
|
|
84
|
+
backendFederation.runtimeFramework === 'effect',
|
|
85
|
+
'strict_effect_required',
|
|
86
|
+
'[BFF][Effect] Backend federation manifest must declare runtimeFramework: "effect".',
|
|
87
|
+
);
|
|
88
|
+
assertManifestAdapter(
|
|
89
|
+
backendFederation.strictEffectApproach === true,
|
|
90
|
+
'strict_effect_required',
|
|
91
|
+
'[BFF][Effect] Backend federation manifest must declare strictEffectApproach: true.',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
assertVersionValue(
|
|
95
|
+
stringValue(backendFederation.contractVersion),
|
|
96
|
+
expected.contractVersion ?? BACKEND_FEDERATION_CONTRACT_VERSION,
|
|
97
|
+
'manifest contractVersion',
|
|
98
|
+
);
|
|
99
|
+
assertVersionValue(
|
|
100
|
+
stringValue(backendFederation.nodeAdapterVersion),
|
|
101
|
+
expected.nodeAdapterVersion ?? BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
|
|
102
|
+
'manifest nodeAdapterVersion',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const boundary = versionBoundaryMetadata(manifest);
|
|
106
|
+
assertVersionValue(
|
|
107
|
+
stringValue(backendFederation.name) ??
|
|
108
|
+
stringValue(manifest.name) ??
|
|
109
|
+
stringValue(manifest.id),
|
|
110
|
+
expected.remoteName,
|
|
111
|
+
'manifest remoteName',
|
|
112
|
+
);
|
|
113
|
+
assertVersionValue(
|
|
114
|
+
stringValue(boundary?.packageName),
|
|
115
|
+
expected.packageName,
|
|
116
|
+
'versionBoundary.packageName',
|
|
117
|
+
);
|
|
118
|
+
assertVersionValue(
|
|
119
|
+
stringValue(boundary?.version),
|
|
120
|
+
expected.version,
|
|
121
|
+
'versionBoundary.version',
|
|
122
|
+
);
|
|
123
|
+
assertVersionValue(
|
|
124
|
+
stringValue(boundary?.buildVersion),
|
|
125
|
+
expected.buildVersion,
|
|
126
|
+
'versionBoundary.buildVersion',
|
|
127
|
+
);
|
|
128
|
+
assertConsistentValue(
|
|
129
|
+
stringValue(manifest.version),
|
|
130
|
+
stringValue(boundary?.version),
|
|
131
|
+
'manifest versionBoundary.version',
|
|
132
|
+
);
|
|
133
|
+
assertConsistentValue(
|
|
134
|
+
stringValue(manifest.buildVersion),
|
|
135
|
+
stringValue(boundary?.buildVersion),
|
|
136
|
+
'manifest versionBoundary.buildVersion',
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const deliveryUnit = manifestDeliveryUnit(backendFederation, boundary);
|
|
140
|
+
assertConsistentValue(
|
|
141
|
+
stringValue(deliveryUnit.boundary?.buildMarker),
|
|
142
|
+
stringValue(boundary?.buildVersion),
|
|
143
|
+
'versionBoundary.deliveryUnit.buildMarker vs versionBoundary.buildVersion',
|
|
144
|
+
);
|
|
145
|
+
assertConsistentValue(
|
|
146
|
+
stringValue(deliveryUnit.top?.unitId),
|
|
147
|
+
stringValue(deliveryUnit.boundary?.unitId),
|
|
148
|
+
'backendFederation.deliveryUnit.unitId vs versionBoundary.deliveryUnit.unitId',
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const manifestUnitId =
|
|
152
|
+
stringValue(deliveryUnit.boundary?.unitId) ??
|
|
153
|
+
stringValue(deliveryUnit.top?.unitId);
|
|
154
|
+
assertVersionValue(manifestUnitId, expected.unitId, 'deliveryUnit.unitId');
|
|
155
|
+
assertVersionValue(
|
|
156
|
+
stringValue(deliveryUnit.boundary?.buildMarker),
|
|
157
|
+
expectedBuildMarker,
|
|
158
|
+
'deliveryUnit.buildMarker',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function validateLoadedBackendFederationContract(
|
|
163
|
+
loaded: BackendFederatedEffectApiModule,
|
|
164
|
+
manifest: BackendFederationManifest,
|
|
165
|
+
remote: BackendFederationRemote,
|
|
166
|
+
) {
|
|
167
|
+
const loadedContract = loaded.backendFederationContract;
|
|
168
|
+
const backendFederation = backendFederationMetadata(manifest);
|
|
169
|
+
const boundary = versionBoundaryMetadata(manifest);
|
|
170
|
+
const compatibility = recordField(loadedContract, 'compatibility');
|
|
171
|
+
|
|
172
|
+
assertManifestAdapter(
|
|
173
|
+
compatibility,
|
|
174
|
+
'version_mismatch',
|
|
175
|
+
`[BFF][Effect] Backend federation expose ${remote.name}/${remote.expose?.replace(
|
|
176
|
+
/^\.\//u,
|
|
177
|
+
'',
|
|
178
|
+
)} must declare compatibility metadata.`,
|
|
179
|
+
);
|
|
180
|
+
assertVersionValue(
|
|
181
|
+
stringValue(loadedContract?.role),
|
|
182
|
+
'microvertical-server',
|
|
183
|
+
'expose role',
|
|
184
|
+
);
|
|
185
|
+
assertVersionValue(
|
|
186
|
+
stringValue(loadedContract?.name),
|
|
187
|
+
remote.name,
|
|
188
|
+
'expose name',
|
|
189
|
+
);
|
|
190
|
+
assertVersionValue(
|
|
191
|
+
stringValue(compatibility.contractVersion),
|
|
192
|
+
stringValue(backendFederation?.contractVersion),
|
|
193
|
+
'expose contractVersion',
|
|
194
|
+
);
|
|
195
|
+
assertVersionValue(
|
|
196
|
+
stringValue(compatibility.nodeAdapterVersion),
|
|
197
|
+
stringValue(backendFederation?.nodeAdapterVersion),
|
|
198
|
+
'expose nodeAdapterVersion',
|
|
199
|
+
);
|
|
200
|
+
assertVersionValue(
|
|
201
|
+
stringValue(compatibility.packageName),
|
|
202
|
+
stringValue(boundary?.packageName),
|
|
203
|
+
'expose packageName',
|
|
204
|
+
);
|
|
205
|
+
assertVersionValue(
|
|
206
|
+
stringValue(compatibility.build),
|
|
207
|
+
stringValue(boundary?.buildVersion),
|
|
208
|
+
'expose buildVersion',
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const deliveryUnit = manifestDeliveryUnit(backendFederation, boundary);
|
|
212
|
+
const manifestUnitId =
|
|
213
|
+
stringValue(deliveryUnit.boundary?.unitId) ??
|
|
214
|
+
stringValue(deliveryUnit.top?.unitId);
|
|
215
|
+
const manifestBuildMarker = stringValue(deliveryUnit.boundary?.buildMarker);
|
|
216
|
+
|
|
217
|
+
// Only enforced when both sides declare the field: absence on either side
|
|
218
|
+
// is a legacy manifest/expose and must stay backward compatible.
|
|
219
|
+
assertConsistentValue(
|
|
220
|
+
manifestUnitId,
|
|
221
|
+
stringValue(compatibility.unitId),
|
|
222
|
+
'deliveryUnit.unitId vs expose compatibility.unitId',
|
|
223
|
+
);
|
|
224
|
+
assertConsistentValue(
|
|
225
|
+
manifestBuildMarker,
|
|
226
|
+
stringValue(compatibility.build),
|
|
227
|
+
'deliveryUnit.buildMarker vs expose compatibility.build',
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function classifyLoadError(error: unknown) {
|
|
232
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
233
|
+
if (
|
|
234
|
+
(typeof error === 'object' &&
|
|
235
|
+
error !== null &&
|
|
236
|
+
'code' in error &&
|
|
237
|
+
error.code === 'timeout') ||
|
|
238
|
+
/timed out/iu.test(message)
|
|
239
|
+
) {
|
|
240
|
+
return 'timeout' as const;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (message.includes('delivery-unit identity mismatch')) {
|
|
244
|
+
return 'version_mismatch' as const;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (
|
|
248
|
+
message.includes('strictEffectApproach') ||
|
|
249
|
+
message.includes('runtimeFramework')
|
|
250
|
+
) {
|
|
251
|
+
return 'strict_effect_required' as const;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return 'remote_unavailable' as const;
|
|
255
|
+
}
|