@bleedingdev/modern-js-plugin-bff 3.5.0-ultramodern.98 → 3.8.1-ultramodern.1
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/dist/cjs/cli/compress.js +1 -1
- package/dist/cjs/cli/generator.js +106 -97
- package/dist/cjs/cli/serverGlobalVars.js +43 -57
- package/dist/cjs/cli.js +6 -2
- package/dist/cjs/loader.js +6 -5
- package/dist/cjs/runtime/effect/adapter/cross-project-policy.js +17 -12
- package/dist/cjs/runtime/effect/adapter/entry.js +13 -5
- package/dist/cjs/runtime/effect/adapter/error-response.js +11 -9
- package/dist/cjs/runtime/effect/adapter.js +2 -2
- package/dist/cjs/runtime/effect/backend-federation/load.js +26 -17
- package/dist/cjs/runtime/effect/backend-federation/plugin.js +12 -2
- package/dist/cjs/runtime/effect/backend-federation/remotes.js +5 -4
- package/dist/cjs/runtime/effect/backend-federation/runtime.js +80 -16
- package/dist/cjs/runtime/effect/edge-dispatcher.js +148 -0
- package/dist/cjs/runtime/effect/edge.js +19 -100
- package/dist/cjs/runtime/effect/endpoint-contracts.js +6 -1
- package/dist/cjs/runtime/effect/handler/definition.js +1 -0
- package/dist/cjs/runtime/effect/handler/http.js +17 -6
- package/dist/cjs/runtime/hono/adapter.js +32 -79
- package/dist/cjs/server.js +19 -30
- package/dist/cjs/utils/client-generator/write-package.js +1 -1
- package/dist/cjs/utils/crossProjectApiPlugin.js +21 -2
- package/dist/cjs/utils/effect-client-generator/generator.js +17 -16
- package/dist/cjs/utils/effect-client-generator/paths.js +4 -15
- package/dist/cjs/utils/effect-client-generator/rendering.js +8 -19
- package/dist/cjs/utils/effect-client-generator/runtime.js +15 -32
- package/dist/cjs/utils/effectSourceLoader.js +70 -10
- package/dist/cjs/utils/pluginGenerator.js +4 -4
- package/dist/esm/cli/compress.mjs +1 -1
- package/dist/esm/cli/generator.mjs +108 -98
- package/dist/esm/cli/serverGlobalVars.mjs +44 -48
- package/dist/esm/cli.mjs +2 -0
- package/dist/esm/loader.mjs +6 -5
- package/dist/esm/runtime/effect/adapter/cross-project-policy.mjs +17 -12
- package/dist/esm/runtime/effect/adapter/entry.mjs +13 -5
- package/dist/esm/runtime/effect/adapter/error-response.mjs +11 -9
- package/dist/esm/runtime/effect/adapter.mjs +3 -3
- package/dist/esm/runtime/effect/backend-federation/load.mjs +26 -17
- package/dist/esm/runtime/effect/backend-federation/plugin.mjs +12 -2
- package/dist/esm/runtime/effect/backend-federation/remotes.mjs +5 -4
- package/dist/esm/runtime/effect/backend-federation/runtime.mjs +80 -16
- package/dist/esm/runtime/effect/edge-dispatcher.mjs +38 -0
- package/dist/esm/runtime/effect/edge.mjs +1 -38
- package/dist/esm/runtime/effect/endpoint-contracts.mjs +6 -1
- package/dist/esm/runtime/effect/handler/definition.mjs +1 -0
- package/dist/esm/runtime/effect/handler/http.mjs +17 -6
- package/dist/esm/runtime/hono/adapter.mjs +33 -80
- package/dist/esm/server.mjs +18 -29
- package/dist/esm/utils/client-generator/write-package.mjs +1 -1
- package/dist/esm/utils/crossProjectApiPlugin.mjs +16 -3
- package/dist/esm/utils/effect-client-generator/generator.mjs +17 -16
- package/dist/esm/utils/effect-client-generator/paths.mjs +5 -6
- package/dist/esm/utils/effect-client-generator/rendering.mjs +9 -10
- package/dist/esm/utils/effect-client-generator/runtime.mjs +16 -23
- package/dist/esm/utils/effectSourceLoader.mjs +69 -12
- package/dist/esm/utils/pluginGenerator.mjs +5 -5
- package/dist/esm-node/cli/compress.mjs +1 -1
- package/dist/esm-node/cli/generator.mjs +108 -98
- package/dist/esm-node/cli/serverGlobalVars.mjs +44 -48
- package/dist/esm-node/cli.mjs +2 -0
- package/dist/esm-node/loader.mjs +6 -5
- package/dist/esm-node/runtime/effect/adapter/cross-project-policy.mjs +17 -12
- package/dist/esm-node/runtime/effect/adapter/entry.mjs +13 -5
- package/dist/esm-node/runtime/effect/adapter/error-response.mjs +11 -9
- package/dist/esm-node/runtime/effect/adapter.mjs +3 -3
- package/dist/esm-node/runtime/effect/backend-federation/load.mjs +26 -17
- package/dist/esm-node/runtime/effect/backend-federation/plugin.mjs +12 -2
- package/dist/esm-node/runtime/effect/backend-federation/remotes.mjs +5 -4
- package/dist/esm-node/runtime/effect/backend-federation/runtime.mjs +80 -16
- package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +39 -0
- package/dist/esm-node/runtime/effect/edge.mjs +1 -38
- package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +6 -1
- package/dist/esm-node/runtime/effect/handler/definition.mjs +1 -0
- package/dist/esm-node/runtime/effect/handler/http.mjs +17 -6
- package/dist/esm-node/runtime/hono/adapter.mjs +33 -80
- package/dist/esm-node/server.mjs +18 -29
- package/dist/esm-node/utils/client-generator/write-package.mjs +1 -1
- package/dist/esm-node/utils/crossProjectApiPlugin.mjs +16 -3
- package/dist/esm-node/utils/effect-client-generator/generator.mjs +17 -16
- package/dist/esm-node/utils/effect-client-generator/paths.mjs +5 -6
- package/dist/esm-node/utils/effect-client-generator/rendering.mjs +9 -10
- package/dist/esm-node/utils/effect-client-generator/runtime.mjs +16 -23
- package/dist/esm-node/utils/effectSourceLoader.mjs +69 -12
- package/dist/esm-node/utils/pluginGenerator.mjs +5 -5
- package/dist/types/cli/generator.d.ts +2 -2
- package/dist/types/cli/serverGlobalVars.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/runtime/effect/adapter/cross-project-policy.d.ts +1 -1
- package/dist/types/runtime/effect/backend-federation/runtime.d.ts +1 -1
- package/dist/types/runtime/effect/edge-dispatcher.d.ts +26 -0
- package/dist/types/runtime/effect/edge.d.ts +1 -26
- package/dist/types/runtime/effect/endpoint-contracts.d.ts +6 -0
- package/dist/types/runtime/effect/handler/definition.d.ts +2 -12
- package/dist/types/runtime/effect/handler/http.d.ts +3 -2
- package/dist/types/runtime/effect/handler/openapi.d.ts +1 -1
- package/dist/types/runtime/effect/handler/types.d.ts +10 -5
- package/dist/types/runtime/effect-client/index.d.ts +1 -1
- package/dist/types/runtime/hono/adapter.d.ts +14 -8
- package/dist/types/utils/crossProjectApiPlugin.d.ts +2 -0
- package/dist/types/utils/effectSourceLoader.d.ts +10 -0
- package/dist/types/utils/pluginGenerator.d.ts +3 -1
- package/package.json +44 -16
|
@@ -22,23 +22,31 @@ function relativeAppPath(appDirectory, entryPath) {
|
|
|
22
22
|
const relativePath = path.relative(appDirectory, entryPath);
|
|
23
23
|
return '..' === relativePath || relativePath.startsWith(`..${path.sep}`) ? void 0 : relativePath;
|
|
24
24
|
}
|
|
25
|
+
function isDependencyPath(relativePath) {
|
|
26
|
+
return relativePath?.split(/[\\/]/u).includes('node_modules') ?? false;
|
|
27
|
+
}
|
|
25
28
|
function resolveEffectAdapterEntryFile(api) {
|
|
26
29
|
const { appDirectory, apiDirectory, distDirectory } = api.getServerContext();
|
|
27
30
|
const appRoot = path.resolve(appDirectory || process.cwd());
|
|
28
|
-
const productionRoot = isProd() && distDirectory ? distDirectory : void 0;
|
|
31
|
+
const productionRoot = isProd() && distDirectory ? path.resolve(distDirectory) : void 0;
|
|
29
32
|
const bffConfig = api.getServerConfig()?.bff;
|
|
30
33
|
const configuredEntry = bffConfig?.effect?.entry;
|
|
31
34
|
if (configuredEntry) {
|
|
32
35
|
const sourceEntry = path.isAbsolute(configuredEntry) ? configuredEntry : path.resolve(appRoot, configuredEntry);
|
|
33
36
|
if (productionRoot) {
|
|
34
37
|
const relativeEntry = relativeAppPath(appRoot, sourceEntry);
|
|
38
|
+
if (void 0 === relativeEntry || isDependencyPath(relativeEntry)) return resolveJsOrTsEntry(sourceEntry);
|
|
35
39
|
return relativeEntry ? resolveJsOrTsEntry(builtEntryPath(path.resolve(productionRoot, relativeEntry))) : void 0;
|
|
36
40
|
}
|
|
37
41
|
return resolveJsOrTsEntry(sourceEntry);
|
|
38
42
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const resolvedApiDirectory = path.resolve(appRoot, apiDirectory || API_DIR);
|
|
44
|
+
const productionApiDirectory = productionRoot ? void 0 !== relativeAppPath(productionRoot, resolvedApiDirectory) ? resolvedApiDirectory : (()=>{
|
|
45
|
+
const relativeApiDirectory = relativeAppPath(appRoot, resolvedApiDirectory);
|
|
46
|
+
if (isDependencyPath(relativeApiDirectory)) return resolvedApiDirectory;
|
|
47
|
+
return relativeApiDirectory ? path.resolve(productionRoot, relativeApiDirectory) : void 0;
|
|
48
|
+
})() : resolvedApiDirectory;
|
|
49
|
+
if (!productionApiDirectory) return;
|
|
50
|
+
return resolveJsOrTsEntry(path.resolve(productionApiDirectory, 'index'));
|
|
43
51
|
}
|
|
44
52
|
export { resolveEffectAdapterEntryFile };
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { logger } from "@modern-js/utils";
|
|
2
2
|
import { createSafeFailureResponse } from "../../safe-failure.mjs";
|
|
3
|
-
|
|
3
|
+
function createEffectAdapterRuntimeErrorResponse(api, error, c) {
|
|
4
4
|
try {
|
|
5
5
|
const serverConfig = api.getServerConfig();
|
|
6
6
|
const onErrorHandler = serverConfig?.onError;
|
|
7
|
-
if (onErrorHandler) {
|
|
7
|
+
if (void 0 !== onErrorHandler) {
|
|
8
8
|
const onErrorContext = ensureJsonContext(c);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
return Promise.resolve(onErrorHandler(error instanceof Error ? error : new Error(String(error)), onErrorContext)).then((result)=>result instanceof Response ? result : createSafeFailureResponse(error)).catch((configError)=>{
|
|
10
|
+
logger.error(`Error in serverConfig.onError handler: ${String(configError)}`);
|
|
11
|
+
return createSafeFailureResponse(error);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
logger.error(error instanceof Error ? error : new Error(String(error)));
|
|
12
15
|
} catch (configError) {
|
|
13
16
|
logger.error(`Error in serverConfig.onError handler: ${configError}`);
|
|
14
17
|
}
|
|
15
|
-
return createSafeFailureResponse(error);
|
|
18
|
+
return Promise.resolve(createSafeFailureResponse(error));
|
|
16
19
|
}
|
|
17
20
|
function ensureJsonContext(c) {
|
|
18
|
-
|
|
19
|
-
if ('function' == typeof maybeJsonContext.json) return c;
|
|
21
|
+
if ('json' in c && 'function' == typeof c.json) return c;
|
|
20
22
|
const headers = {
|
|
21
23
|
'content-type': 'application/json; charset=utf-8'
|
|
22
24
|
};
|
|
@@ -27,7 +29,7 @@ function ensureJsonContext(c) {
|
|
|
27
29
|
headers: extraHeaders
|
|
28
30
|
} : statusOrInit;
|
|
29
31
|
const responseHeaders = new Headers(headers);
|
|
30
|
-
if (responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
|
|
32
|
+
if (void 0 !== responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
|
|
31
33
|
responseHeaders.set(key, value);
|
|
32
34
|
});
|
|
33
35
|
return new Response(JSON.stringify(data), {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fs, isProd, logger } from "@modern-js/utils";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
|
|
3
|
+
import { loadEffectBuiltModule, loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
|
|
4
4
|
import { before } from "./adapter/constants.mjs";
|
|
5
5
|
import { resolveEffectAdapterCrossProjectPolicy } from "./adapter/cross-project-policy.mjs";
|
|
6
6
|
import { resolveEffectAdapterEntryFile } from "./adapter/entry.mjs";
|
|
@@ -29,10 +29,10 @@ class EffectAdapter {
|
|
|
29
29
|
let mod;
|
|
30
30
|
try {
|
|
31
31
|
const { appDirectory } = this.api.getServerContext();
|
|
32
|
-
mod = await loadEffectSourceModule({
|
|
32
|
+
mod = await (isProd() ? loadEffectBuiltModule(entryFile) : loadEffectSourceModule({
|
|
33
33
|
resourcePath: entryFile,
|
|
34
34
|
appDir: appDirectory || path.dirname(entryFile)
|
|
35
|
-
});
|
|
35
|
+
}));
|
|
36
36
|
} catch (error) {
|
|
37
37
|
logger.error(`[BFF][Effect] Failed to load Effect entry: ${entryFile}\n${String(error)}`);
|
|
38
38
|
this.handler = null;
|
|
@@ -2,27 +2,36 @@ import { BACKEND_FEDERATION_EFFECT_EXPOSE } from "@modern-js/utils/universal";
|
|
|
2
2
|
import { formatBackendFederationIdentityIssues, validateExpectedBackendFederationIdentity } from "./identity.mjs";
|
|
3
3
|
import { createBackendFederationRuntime } from "./runtime.mjs";
|
|
4
4
|
import { isRecord, normalizeExpose } from "./utils.mjs";
|
|
5
|
+
import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
|
|
6
|
+
import * as __rspack_external_effect_Logger_1691a01b from "effect/Logger";
|
|
5
7
|
const LEGACY_LOAD_WARNING = '[BFF][Effect] loadBackendFederatedEffectApi was called without an expected delivery-unit identity (expected.unitId + expected.buildMarker). Identity-less public backend loads are deprecated (ADR-0019/MV-G23) and will be rejected in a future major.';
|
|
6
|
-
|
|
8
|
+
const legacyWarningLogger = __rspack_external_effect_Logger_1691a01b.withLeveledConsole(__rspack_external_effect_Logger_1691a01b.formatLogFmt);
|
|
9
|
+
function warnLegacyLoad() {
|
|
10
|
+
__rspack_external_effect_Effect_41060786.runSync(__rspack_external_effect_Effect_41060786.provideService(__rspack_external_effect_Effect_41060786.logWarning(LEGACY_LOAD_WARNING), __rspack_external_effect_Logger_1691a01b.CurrentLoggers, new Set([
|
|
11
|
+
legacyWarningLogger
|
|
12
|
+
])));
|
|
13
|
+
}
|
|
14
|
+
function loadBackendFederatedEffectApi(options) {
|
|
7
15
|
const remoteName = options.remote?.name ?? options.remoteName;
|
|
8
|
-
if (
|
|
9
|
-
if (void 0 === options.expected)
|
|
16
|
+
if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
|
|
17
|
+
if (void 0 === options.expected) warnLegacyLoad();
|
|
10
18
|
const runtime = options.runtime ?? createBackendFederationRuntime(options);
|
|
11
19
|
const expose = options.expose ?? options.remote?.expose ?? BACKEND_FEDERATION_EFFECT_EXPOSE;
|
|
12
20
|
const remoteRequest = `${remoteName}/${normalizeExpose(expose)}`;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
return runtime.loadRemote(remoteRequest).then((loaded)=>{
|
|
22
|
+
if (!isRecord(loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must load an object module.`);
|
|
23
|
+
const backendContract = loaded.backendFederationContract;
|
|
24
|
+
if (!isRecord(backendContract) || true !== backendContract.strictEffectApproach) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (strictEffectApproach: true).`);
|
|
25
|
+
if ('effect' !== backendContract.runtimeFramework) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (runtimeFramework: "effect").`);
|
|
26
|
+
if ('string' == typeof backendContract.name && backendContract.name !== remoteName) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} metadata name mismatch: expected ${remoteName}, received ${backendContract.name}.`);
|
|
27
|
+
if (!('runtime' in loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose api and runtime.`);
|
|
28
|
+
if (void 0 !== options.expected) {
|
|
29
|
+
const issues = validateExpectedBackendFederationIdentity(loaded, options.expected, {
|
|
30
|
+
allowMissingIdentityMetadata: true === options.allowMissingIdentityMetadata
|
|
31
|
+
});
|
|
32
|
+
if (issues.length > 0) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} delivery-unit identity mismatch: ${formatBackendFederationIdentityIssues(issues)}.`);
|
|
33
|
+
}
|
|
34
|
+
return loaded;
|
|
35
|
+
});
|
|
27
36
|
}
|
|
28
37
|
export { loadBackendFederatedEffectApi };
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
function createBackendFederationLoadEntryPlugin(options) {
|
|
2
2
|
return {
|
|
3
3
|
name: 'modernjs-backend-federation-load-entry',
|
|
4
|
-
|
|
5
|
-
return options.resolveEntry({
|
|
4
|
+
loadEntry ({ remoteInfo }) {
|
|
5
|
+
return Promise.resolve(options.resolveEntry({
|
|
6
6
|
name: remoteInfo.name,
|
|
7
7
|
entry: remoteInfo.entry,
|
|
8
8
|
type: remoteInfo.type,
|
|
9
9
|
entryGlobalName: remoteInfo.entryGlobalName,
|
|
10
10
|
shareScope: remoteInfo.shareScope
|
|
11
|
+
})).then((entry)=>{
|
|
12
|
+
if (void 0 === entry) return;
|
|
13
|
+
return {
|
|
14
|
+
get (id) {
|
|
15
|
+
return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
|
|
16
|
+
},
|
|
17
|
+
init (...args) {
|
|
18
|
+
return entry.init?.(...args);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
11
21
|
});
|
|
12
22
|
}
|
|
13
23
|
};
|
|
@@ -2,10 +2,11 @@ function collectRemotes(options) {
|
|
|
2
2
|
const remotes = [
|
|
3
3
|
...options.remotes ?? []
|
|
4
4
|
];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const configuredRemote = options.remote;
|
|
6
|
+
if (void 0 !== configuredRemote) {
|
|
7
|
+
const existingIndex = remotes.findIndex((remote)=>remote.name === configuredRemote.name);
|
|
8
|
+
if (existingIndex >= 0) remotes[existingIndex] = configuredRemote;
|
|
9
|
+
else remotes.push(configuredRemote);
|
|
9
10
|
}
|
|
10
11
|
return remotes;
|
|
11
12
|
}
|
|
@@ -1,26 +1,90 @@
|
|
|
1
|
+
import { ModuleFederation } from "@module-federation/runtime";
|
|
1
2
|
import { loadBackendFederationExpose } from "./entry.mjs";
|
|
3
|
+
import { createBackendFederationLoadEntryPlugin } from "./plugin.mjs";
|
|
2
4
|
import { collectRemotes } from "./remotes.mjs";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return `./${normalizeExpose(expose)}`;
|
|
5
|
+
function _check_private_redeclaration(obj, privateCollection) {
|
|
6
|
+
if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
6
7
|
}
|
|
8
|
+
function _class_apply_descriptor_get(receiver, descriptor) {
|
|
9
|
+
if (descriptor.get) return descriptor.get.call(receiver);
|
|
10
|
+
return descriptor.value;
|
|
11
|
+
}
|
|
12
|
+
function _class_apply_descriptor_set(receiver, descriptor, value) {
|
|
13
|
+
if (descriptor.set) descriptor.set.call(receiver, value);
|
|
14
|
+
else {
|
|
15
|
+
if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
|
|
16
|
+
descriptor.value = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function _class_extract_field_descriptor(receiver, privateMap, action) {
|
|
20
|
+
if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
|
|
21
|
+
return privateMap.get(receiver);
|
|
22
|
+
}
|
|
23
|
+
function _class_private_field_get(receiver, privateMap) {
|
|
24
|
+
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
|
|
25
|
+
return _class_apply_descriptor_get(receiver, descriptor);
|
|
26
|
+
}
|
|
27
|
+
function _class_private_field_init(obj, privateMap, value) {
|
|
28
|
+
_check_private_redeclaration(obj, privateMap);
|
|
29
|
+
privateMap.set(obj, value);
|
|
30
|
+
}
|
|
31
|
+
function _class_private_field_set(receiver, privateMap, value) {
|
|
32
|
+
var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
|
|
33
|
+
_class_apply_descriptor_set(receiver, descriptor, value);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
let nextRuntimeId = 0;
|
|
7
37
|
function parseRemoteRequest(request) {
|
|
8
38
|
const [remoteName, ...exposeParts] = request.split('/');
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
39
|
+
if (void 0 === remoteName || 0 === remoteName.length || 0 === exposeParts.length) return;
|
|
40
|
+
return remoteName;
|
|
41
|
+
}
|
|
42
|
+
function createLocalEntryPlugin(options, remotes) {
|
|
43
|
+
return createBackendFederationLoadEntryPlugin({
|
|
44
|
+
resolveEntry (resolvedRemote) {
|
|
45
|
+
const remote = remotes.find((candidate)=>candidate.name === resolvedRemote.name);
|
|
46
|
+
if (void 0 === remote) return;
|
|
47
|
+
return {
|
|
48
|
+
get (expose) {
|
|
49
|
+
return ()=>loadBackendFederationExpose(remote, expose, options);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function isolateRegisteredRemoteEntries(remotes) {
|
|
56
|
+
const runtimeId = nextRuntimeId;
|
|
57
|
+
nextRuntimeId += 1;
|
|
58
|
+
return remotes.map((remote)=>({
|
|
59
|
+
...remote,
|
|
60
|
+
entry: `${remote.entry}#modernjs-backend-runtime-${runtimeId}`
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
var _remoteNames = /*#__PURE__*/ new WeakMap();
|
|
64
|
+
class BackendFederationRuntime extends ModuleFederation {
|
|
65
|
+
loadRemote(request, loadOptions) {
|
|
66
|
+
const remoteName = parseRemoteRequest(request);
|
|
67
|
+
if (void 0 === remoteName) return Promise.reject(new Error(`[BFF][Effect] Invalid backend federation request ${request}.`));
|
|
68
|
+
if (!_class_private_field_get(this, _remoteNames).has(remoteName)) return Promise.reject(new Error(`[BFF][Effect] Missing backend federation remote ${remoteName}.`));
|
|
69
|
+
return super.loadRemote(request, loadOptions);
|
|
70
|
+
}
|
|
71
|
+
constructor(options, remotes){
|
|
72
|
+
super({
|
|
73
|
+
name: options.hostName,
|
|
74
|
+
remotes: isolateRegisteredRemoteEntries(remotes),
|
|
75
|
+
shared: {},
|
|
76
|
+
plugins: [
|
|
77
|
+
createLocalEntryPlugin(options, remotes)
|
|
78
|
+
]
|
|
79
|
+
}), _class_private_field_init(this, _remoteNames, {
|
|
80
|
+
writable: true,
|
|
81
|
+
value: void 0
|
|
82
|
+
});
|
|
83
|
+
_class_private_field_set(this, _remoteNames, new Set(remotes.map((remote)=>remote.name)));
|
|
84
|
+
}
|
|
14
85
|
}
|
|
15
86
|
function createBackendFederationRuntime(options) {
|
|
16
87
|
const remotes = collectRemotes(options);
|
|
17
|
-
return
|
|
18
|
-
async loadRemote (request) {
|
|
19
|
-
const { remoteName, expose } = parseRemoteRequest(request);
|
|
20
|
-
const remote = remotes.find((candidate)=>candidate.name === remoteName);
|
|
21
|
-
if (!remote) throw new Error(`[BFF][Effect] Missing backend federation remote ${remoteName}.`);
|
|
22
|
-
return loadBackendFederationExpose(remote, expose, options);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
88
|
+
return new BackendFederationRuntime(options, remotes);
|
|
25
89
|
}
|
|
26
90
|
export { createBackendFederationRuntime };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { dispatchEffectBffRequestWithContext } from "./dispatch.mjs";
|
|
2
|
+
import { runWithEffectContext, useEffectContext, useOperationContext } from "./edge-context.mjs";
|
|
3
|
+
import { resolveEffectBffModuleHandler } from "./module.mjs";
|
|
4
|
+
export * from "./handler.mjs";
|
|
5
|
+
async function dispatchEffectBffRequest(handler, request, options = {}) {
|
|
6
|
+
return dispatchEffectBffRequestWithContext(handler, request, {
|
|
7
|
+
...options,
|
|
8
|
+
runWithEffectContext: runWithEffectContext
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async function createEffectBffEdgeDispatcher(options) {
|
|
12
|
+
const loaded = await resolveEffectBffModuleHandler(options.module, {
|
|
13
|
+
openapi: options.openapi,
|
|
14
|
+
dataPlatform: options.dataPlatform,
|
|
15
|
+
onWarning: options.onWarning
|
|
16
|
+
});
|
|
17
|
+
if (!loaded) throw new Error('[BFF][Effect] Invalid Effect edge module. Export defineEffectBff(...) or a { api, layer } HttpApi module.');
|
|
18
|
+
return {
|
|
19
|
+
dispatch: (request, dispatchOptions = {})=>dispatchEffectBffRequest(loaded.handler, request, {
|
|
20
|
+
...dispatchOptions,
|
|
21
|
+
prefix: options.prefix,
|
|
22
|
+
onError: options.onError
|
|
23
|
+
}),
|
|
24
|
+
dispose: async ()=>{
|
|
25
|
+
await loaded.dispose?.();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const createEffectBffTestHandler = createEffectBffEdgeHandler;
|
|
30
|
+
async function createEffectBffEdgeHandler(options) {
|
|
31
|
+
const dispatcher = await createEffectBffEdgeDispatcher(options);
|
|
32
|
+
return {
|
|
33
|
+
handler: dispatcher.dispatch,
|
|
34
|
+
dispose: dispatcher.dispose
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export { createEffectOperationContext } from "./operation-context.mjs";
|
|
38
|
+
export { createEffectBffEdgeDispatcher, createEffectBffEdgeHandler, createEffectBffTestHandler, dispatchEffectBffRequest, runWithEffectContext, useEffectContext, useOperationContext };
|
|
@@ -1,39 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { runWithEffectContext, useEffectContext, useOperationContext } from "./edge-context.mjs";
|
|
3
|
-
import { resolveEffectBffModuleHandler } from "./module.mjs";
|
|
4
|
-
export * from "./handler.mjs";
|
|
5
|
-
async function dispatchEffectBffRequest(handler, request, options = {}) {
|
|
6
|
-
return dispatchEffectBffRequestWithContext(handler, request, {
|
|
7
|
-
...options,
|
|
8
|
-
runWithEffectContext: runWithEffectContext
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
async function createEffectBffEdgeDispatcher(options) {
|
|
12
|
-
const loaded = await resolveEffectBffModuleHandler(options.module, {
|
|
13
|
-
openapi: options.openapi,
|
|
14
|
-
dataPlatform: options.dataPlatform,
|
|
15
|
-
onWarning: options.onWarning
|
|
16
|
-
});
|
|
17
|
-
if (!loaded) throw new Error('[BFF][Effect] Invalid Effect edge module. Export defineEffectBff(...) or a { api, layer } HttpApi module.');
|
|
18
|
-
return {
|
|
19
|
-
dispatch: (request, dispatchOptions = {})=>dispatchEffectBffRequest(loaded.handler, request, {
|
|
20
|
-
...dispatchOptions,
|
|
21
|
-
prefix: options.prefix,
|
|
22
|
-
onError: options.onError
|
|
23
|
-
}),
|
|
24
|
-
dispose: async ()=>{
|
|
25
|
-
await loaded.dispose?.();
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const createEffectBffTestHandler = createEffectBffEdgeHandler;
|
|
30
|
-
async function createEffectBffEdgeHandler(options) {
|
|
31
|
-
const dispatcher = await createEffectBffEdgeDispatcher(options);
|
|
32
|
-
return {
|
|
33
|
-
handler: dispatcher.dispatch,
|
|
34
|
-
dispose: dispatcher.dispose
|
|
35
|
-
};
|
|
36
|
-
}
|
|
1
|
+
export * from "./edge-dispatcher.mjs";
|
|
37
2
|
export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, createBackendFederationLoadEntryPlugin, createBackendFederationRuntime, loadBackendFederatedEffectApi, validateExpectedBackendFederationIdentity } from "./backend-federation.mjs";
|
|
38
|
-
export { createEffectOperationContext } from "./operation-context.mjs";
|
|
39
|
-
export { createEffectBffEdgeDispatcher, createEffectBffEdgeHandler, createEffectBffTestHandler, dispatchEffectBffRequest, runWithEffectContext, useEffectContext, useOperationContext };
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { createOperationContractHash } from "@modern-js/bff-core";
|
|
2
2
|
import { classifyEffectBffEntryModule, isValidatorAwareHandlerFactory } from "./entry-shape.mjs";
|
|
3
|
+
function resolveEffectEndpointName(endpoint) {
|
|
4
|
+
if ('string' == typeof endpoint.identifier && endpoint.identifier) return endpoint.identifier;
|
|
5
|
+
if ('string' == typeof endpoint.name && endpoint.name) return endpoint.name;
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
3
8
|
function ensureLeadingSlash(pathname) {
|
|
4
9
|
return pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
5
10
|
}
|
|
@@ -28,7 +33,7 @@ function collectEffectEndpoints(reflect, api, prefix) {
|
|
|
28
33
|
endpoints.push({
|
|
29
34
|
apiId,
|
|
30
35
|
groupName: String(group.identifier),
|
|
31
|
-
endpointName:
|
|
36
|
+
endpointName: resolveEffectEndpointName(endpoint),
|
|
32
37
|
method: String(endpoint.method).toUpperCase(),
|
|
33
38
|
routePath: getEffectRoutePath(prefix, String(endpoint.path))
|
|
34
39
|
});
|
|
@@ -16,6 +16,7 @@ function defineEffectBff(definition) {
|
|
|
16
16
|
openapi: options?.openapi,
|
|
17
17
|
rpc: mergedRpcOptions,
|
|
18
18
|
dataPlatform: mergeDataPlatformOptions(definition.dataPlatform, options?.dataPlatform),
|
|
19
|
+
interceptRequest: definition.interceptRequest,
|
|
19
20
|
validateRequest: options?.validateRequest
|
|
20
21
|
});
|
|
21
22
|
};
|
|
@@ -12,23 +12,30 @@ function createHttpApiHandler(options) {
|
|
|
12
12
|
const openApiLayer = createOpenApiLayer(options.api, options.openapi);
|
|
13
13
|
const mergedLayer = openApiLayer ? __rspack_external_effect_Layer_69c46453.mergeAll(apiLayer, openApiLayer) : apiLayer;
|
|
14
14
|
const httpApiHandler = HttpRouter.toWebHandler(mergedLayer);
|
|
15
|
-
const
|
|
16
|
-
const policyDenial = options.validateRequest?.(request);
|
|
17
|
-
if (policyDenial) return policyDenial;
|
|
15
|
+
const dispatchHttpApiRequest = async (request, context)=>{
|
|
18
16
|
const preparedRequest = await prepareJsonRequestBody(request);
|
|
19
17
|
if (preparedRequest instanceof Response) return preparedRequest;
|
|
20
18
|
const validationError = validateDataPlatformRequestEnvelope(preparedRequest, options.dataPlatform);
|
|
21
19
|
if (validationError) return validationError;
|
|
22
20
|
return httpApiHandler.handler(preparedRequest, toEffectServiceContext(context));
|
|
23
21
|
};
|
|
22
|
+
const withRequestPolicy = async (request, context)=>{
|
|
23
|
+
const policyDenial = options.validateRequest?.(request);
|
|
24
|
+
if (policyDenial) return policyDenial;
|
|
25
|
+
const next = ()=>dispatchHttpApiRequest(request, context);
|
|
26
|
+
return options.interceptRequest ? options.interceptRequest({
|
|
27
|
+
request,
|
|
28
|
+
next
|
|
29
|
+
}) : next();
|
|
30
|
+
};
|
|
24
31
|
const batchHandler = createDataPlatformBatchRequestHandler({
|
|
25
32
|
dataPlatform: options.dataPlatform,
|
|
26
|
-
handleItem:
|
|
33
|
+
handleItem: withRequestPolicy
|
|
27
34
|
});
|
|
28
35
|
const handleHttpApiRequest = async (request, context)=>{
|
|
29
36
|
const pathname = getRequestPathname(request);
|
|
30
37
|
if (batchHandler.enabled && pathname === batchHandler.path) return batchHandler.handle(request, context);
|
|
31
|
-
return
|
|
38
|
+
return withRequestPolicy(request, context);
|
|
32
39
|
};
|
|
33
40
|
if (!options.rpc) return {
|
|
34
41
|
handler: handleHttpApiRequest,
|
|
@@ -43,7 +50,11 @@ function createHttpApiHandler(options) {
|
|
|
43
50
|
if (isRpcRequest(request, rpcPath)) {
|
|
44
51
|
const policyDenial = options.validateRequest?.(request);
|
|
45
52
|
if (policyDenial) return policyDenial;
|
|
46
|
-
|
|
53
|
+
const next = ()=>rpcHandler.handler(request, toEffectServiceContext(context));
|
|
54
|
+
return options.interceptRequest ? options.interceptRequest({
|
|
55
|
+
request,
|
|
56
|
+
next
|
|
57
|
+
}) : next();
|
|
47
58
|
}
|
|
48
59
|
return handleHttpApiRequest(request, context);
|
|
49
60
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isProd, logger } from "@modern-js/utils";
|
|
1
|
+
import { logger } from "@modern-js/utils";
|
|
3
2
|
import createHonoRoutes from "../../utils/createHonoRoutes.mjs";
|
|
4
3
|
import { checkCrossProjectPolicyResponse, resolveAdapterCrossProjectPolicy } from "../../utils/crossProjectServerPolicy.mjs";
|
|
5
4
|
import { createSafeFailureResponse } from "../safe-failure.mjs";
|
|
@@ -8,17 +7,9 @@ const before = [
|
|
|
8
7
|
'custom-server-middleware',
|
|
9
8
|
'render'
|
|
10
9
|
];
|
|
11
|
-
const kParentHonoVars = Symbol.for('modernjs.hono.parentVars');
|
|
12
10
|
class HonoAdapter {
|
|
13
|
-
wrapInArray(handler) {
|
|
14
|
-
if (Array.isArray(handler)) return handler;
|
|
15
|
-
return [
|
|
16
|
-
handler
|
|
17
|
-
];
|
|
18
|
-
}
|
|
19
11
|
constructor(api){
|
|
20
12
|
this.apiMiddleware = [];
|
|
21
|
-
this.apiServer = null;
|
|
22
13
|
this.isHono = true;
|
|
23
14
|
this.prefix = '/api';
|
|
24
15
|
this.setHandlers = async ()=>{
|
|
@@ -29,7 +20,7 @@ class HonoAdapter {
|
|
|
29
20
|
name: 'hono-bff-api',
|
|
30
21
|
path,
|
|
31
22
|
method,
|
|
32
|
-
handler,
|
|
23
|
+
handler: this.withErrorBoundary(handler),
|
|
33
24
|
order: 'post',
|
|
34
25
|
before: before
|
|
35
26
|
}));
|
|
@@ -50,85 +41,47 @@ class HonoAdapter {
|
|
|
50
41
|
});
|
|
51
42
|
}
|
|
52
43
|
};
|
|
53
|
-
this.
|
|
54
|
-
if (!this.isHono) return;
|
|
55
|
-
this.apiServer = new Hono();
|
|
56
|
-
this.apiServer.use('*', run);
|
|
57
|
-
this.apiServer.use('*', async (c, next)=>{
|
|
58
|
-
const nodeReq = c.env?.node?.req;
|
|
59
|
-
const parentVars = nodeReq?.[kParentHonoVars];
|
|
60
|
-
if (parentVars && 'object' == typeof parentVars) {
|
|
61
|
-
delete nodeReq[kParentHonoVars];
|
|
62
|
-
for (const [key, value] of Object.entries(parentVars))if (void 0 === c.get(key)) c.set(key, value);
|
|
63
|
-
}
|
|
64
|
-
await next();
|
|
65
|
-
});
|
|
66
|
-
this.apiMiddleware.forEach(({ path = '*', method = 'all', handler })=>{
|
|
67
|
-
const handlers = this.wrapInArray(handler);
|
|
68
|
-
if (0 === handlers.length) return;
|
|
69
|
-
const firstHandler = handlers[0];
|
|
70
|
-
const restHandlers = handlers.slice(1);
|
|
71
|
-
const m = method;
|
|
72
|
-
const server = this.apiServer;
|
|
73
|
-
if (!server) return;
|
|
74
|
-
const register = server[m];
|
|
75
|
-
register.call(server, path, firstHandler, ...restHandlers);
|
|
76
|
-
});
|
|
77
|
-
this.apiServer.onError(async (err, c)=>{
|
|
78
|
-
try {
|
|
79
|
-
const serverConfig = this.api.getServerConfig();
|
|
80
|
-
const onErrorHandler = serverConfig?.onError;
|
|
81
|
-
if (onErrorHandler) {
|
|
82
|
-
const result = await onErrorHandler(err, c);
|
|
83
|
-
if (result instanceof Response) return result;
|
|
84
|
-
} else logger.error(err);
|
|
85
|
-
} catch (configError) {
|
|
86
|
-
logger.error(`Error in serverConfig.onError handler: ${configError}`);
|
|
87
|
-
}
|
|
88
|
-
return createSafeFailureResponse(err);
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
this.registerMiddleware = async (options)=>{
|
|
92
|
-
const { prefix } = options;
|
|
44
|
+
this.registerMiddleware = async (options = {})=>{
|
|
93
45
|
const { bffRuntimeFramework } = this.api.getServerContext();
|
|
94
46
|
if ('hono' !== bffRuntimeFramework) {
|
|
95
47
|
this.isHono = false;
|
|
96
48
|
return;
|
|
97
49
|
}
|
|
50
|
+
const { prefix } = options;
|
|
98
51
|
this.prefix = prefix || this.prefix;
|
|
99
52
|
const { middlewares: globalMiddlewares } = this.api.getServerContext();
|
|
100
53
|
await this.setHandlers();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const nodeReq = c.env?.node?.req;
|
|
113
|
-
if (nodeReq) {
|
|
114
|
-
const parentVars = c?.var;
|
|
115
|
-
nodeReq[kParentHonoVars] = parentVars && 'object' == typeof parentVars ? {
|
|
116
|
-
...parentVars
|
|
117
|
-
} : parentVars;
|
|
118
|
-
}
|
|
119
|
-
const response = await this.apiServer.fetch(c.req.raw, c.env);
|
|
120
|
-
if (404 !== response.status) return new Response(response.body, response);
|
|
121
|
-
}
|
|
122
|
-
await next();
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
globalMiddlewares.push(dynamicApiMiddleware);
|
|
54
|
+
globalMiddlewares.push(...this.apiMiddleware);
|
|
55
|
+
};
|
|
56
|
+
this.handleRouteError = async (error, c)=>{
|
|
57
|
+
try {
|
|
58
|
+
const onErrorHandler = this.api.getServerConfig()?.onError;
|
|
59
|
+
if (onErrorHandler) {
|
|
60
|
+
const result = await onErrorHandler(error, c);
|
|
61
|
+
if (result instanceof Response) return result;
|
|
62
|
+
} else logger.error(error);
|
|
63
|
+
} catch (configError) {
|
|
64
|
+
logger.error(`Error in serverConfig.onError handler: ${configError}`);
|
|
126
65
|
}
|
|
66
|
+
return createSafeFailureResponse(error);
|
|
127
67
|
};
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
68
|
+
this.withErrorBoundary = (handler)=>{
|
|
69
|
+
const handlers = Array.isArray(handler) ? handler : [
|
|
70
|
+
handler
|
|
71
|
+
];
|
|
72
|
+
const [firstHandler, ...restHandlers] = handlers;
|
|
73
|
+
if (!firstHandler) return handler;
|
|
74
|
+
const boundary = async (c, next)=>{
|
|
75
|
+
try {
|
|
76
|
+
return await firstHandler(c, next);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return this.handleRouteError(error, c);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return Array.isArray(handler) ? [
|
|
82
|
+
boundary,
|
|
83
|
+
...restHandlers
|
|
84
|
+
] : boundary;
|
|
132
85
|
};
|
|
133
86
|
this.api = api;
|
|
134
87
|
}
|