@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,140 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
import { dispatchEffectBffRequest, resolveEffectBffModuleHandler, useEffectContext } from "@modern-js/bff-effect/effect";
|
|
4
|
+
import { createDisposableServerRuntimeHandle, registerServerRuntimeDisposer } from "@modern-js/server-runtime-extensions/runtime-lifecycle";
|
|
5
|
+
import { fs, isProd, logger } from "@modern-js/utils";
|
|
6
|
+
import { checkCrossProjectPolicyForRequest } from "../cross-project-policy/evaluation.mjs";
|
|
7
|
+
import { loadEffectBuiltModule, loadEffectSourceModule } from "../effect-source-loader/loader.mjs";
|
|
8
|
+
import { resolveEffectAdapterCrossProjectPolicy } from "./cross-project-policy.mjs";
|
|
9
|
+
import { resolveEffectAdapterEntryFile } from "./entry.mjs";
|
|
10
|
+
import { createEffectAdapterRuntimeErrorResponse } from "./error-response.mjs";
|
|
11
|
+
const EFFECT_MIDDLEWARE_BEFORE = [
|
|
12
|
+
'custom-server-hook',
|
|
13
|
+
'custom-server-middleware',
|
|
14
|
+
'render'
|
|
15
|
+
];
|
|
16
|
+
class EffectAdapter {
|
|
17
|
+
async loadHandler() {
|
|
18
|
+
const entryFile = resolveEffectAdapterEntryFile(this.api);
|
|
19
|
+
if (!entryFile || !await fs.pathExists(entryFile)) {
|
|
20
|
+
this.handler = null;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let mod;
|
|
24
|
+
try {
|
|
25
|
+
const { appDirectory } = this.api.getServerContext();
|
|
26
|
+
mod = await (isProd() ? loadEffectBuiltModule(entryFile) : loadEffectSourceModule({
|
|
27
|
+
resourcePath: entryFile,
|
|
28
|
+
appDir: appDirectory || node_path.dirname(entryFile)
|
|
29
|
+
}));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.error(`[BFF][Effect] Failed to load Effect entry: ${entryFile}\n${String(error)}`);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
const crossProjectPolicies = new Map(await Promise.all(this.prefixes.map(async (prefix)=>[
|
|
35
|
+
prefix,
|
|
36
|
+
await resolveEffectAdapterCrossProjectPolicy(this.api, prefix, mod)
|
|
37
|
+
])));
|
|
38
|
+
const effectConfig = this.api.getServerConfig()?.bff?.effect;
|
|
39
|
+
const loaded = await resolveEffectBffModuleHandler(mod, {
|
|
40
|
+
openapi: effectConfig?.openapi,
|
|
41
|
+
dataPlatform: effectConfig?.dataPlatform,
|
|
42
|
+
validateRequest: (request)=>{
|
|
43
|
+
const { path: mountedPathname } = useEffectContext();
|
|
44
|
+
const prefix = this.matchingPrefixes.find((item)=>'/' === item || mountedPathname === item || mountedPathname.startsWith(`${item}/`));
|
|
45
|
+
if (!prefix) return null;
|
|
46
|
+
const requestUrl = new URL(request.url);
|
|
47
|
+
if ('/' !== prefix) requestUrl.pathname = '/' === requestUrl.pathname ? prefix : `${prefix}${requestUrl.pathname}`;
|
|
48
|
+
return checkCrossProjectPolicyForRequest(new Request(requestUrl, request), crossProjectPolicies.get(prefix));
|
|
49
|
+
},
|
|
50
|
+
onWarning: (message)=>logger.warn(message)
|
|
51
|
+
});
|
|
52
|
+
if (!loaded) {
|
|
53
|
+
const error = new Error(`[BFF][Effect] Invalid Effect entry module: ${entryFile}. Export defineEffectBff(...) or a { api, layer } HttpApi module.`);
|
|
54
|
+
logger.warn(error.message);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
const candidateOwner = {};
|
|
58
|
+
if (loaded.dispose) registerServerRuntimeDisposer(candidateOwner, loaded.dispose);
|
|
59
|
+
const candidate = createDisposableServerRuntimeHandle(candidateOwner, loaded.handler);
|
|
60
|
+
if (this.retired) {
|
|
61
|
+
await candidate.dispose();
|
|
62
|
+
throw new Error('Cannot initialize a retired Effect adapter.');
|
|
63
|
+
}
|
|
64
|
+
this.handler = candidate;
|
|
65
|
+
}
|
|
66
|
+
constructor(api){
|
|
67
|
+
this.handler = null;
|
|
68
|
+
this.prefix = '/api';
|
|
69
|
+
this.prefixes = [
|
|
70
|
+
'/api'
|
|
71
|
+
];
|
|
72
|
+
this.matchingPrefixes = [
|
|
73
|
+
'/api'
|
|
74
|
+
];
|
|
75
|
+
this.retired = false;
|
|
76
|
+
this.registerMiddleware = async (options)=>{
|
|
77
|
+
const { enableHandleWeb } = options;
|
|
78
|
+
const { bffRuntimeFramework, middlewares: globalMiddlewares } = this.api.getServerContext();
|
|
79
|
+
if ('hono' === bffRuntimeFramework) return;
|
|
80
|
+
const configuredPrefixes = Array.isArray(options.prefix) ? options.prefix.filter(Boolean) : [
|
|
81
|
+
options.prefix || '/api'
|
|
82
|
+
];
|
|
83
|
+
this.prefixes = [
|
|
84
|
+
...new Set(configuredPrefixes.length > 0 ? configuredPrefixes : [
|
|
85
|
+
'/api'
|
|
86
|
+
])
|
|
87
|
+
];
|
|
88
|
+
this.matchingPrefixes = [
|
|
89
|
+
...this.prefixes
|
|
90
|
+
].sort((left, right)=>right.length - left.length);
|
|
91
|
+
this.prefix = this.prefixes[0] || '/api';
|
|
92
|
+
const { serverBase } = this.api.getServerContext();
|
|
93
|
+
if (serverBase) this.unregisterRuntimeDisposer = registerServerRuntimeDisposer(serverBase, this.dispose);
|
|
94
|
+
try {
|
|
95
|
+
await this.loadHandler();
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await this.dispose();
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
const middlewarePrefixes = enableHandleWeb ? [
|
|
101
|
+
this.prefix
|
|
102
|
+
] : this.matchingPrefixes;
|
|
103
|
+
const middlewares = middlewarePrefixes.map((middlewarePrefix)=>({
|
|
104
|
+
name: 'effect-api-handler',
|
|
105
|
+
path: enableHandleWeb ? '*' : `${middlewarePrefix}/*`,
|
|
106
|
+
method: 'all',
|
|
107
|
+
order: 'post',
|
|
108
|
+
before: EFFECT_MIDDLEWARE_BEFORE,
|
|
109
|
+
handler: async (context, next)=>{
|
|
110
|
+
const handler = this.handler;
|
|
111
|
+
if (!handler) {
|
|
112
|
+
if (enableHandleWeb) return void await next();
|
|
113
|
+
return createEffectAdapterRuntimeErrorResponse(this.api, new Error('[BFF][Effect] Missing Effect entry. Define api/index or configure bff.effect.entry.'), context);
|
|
114
|
+
}
|
|
115
|
+
const prefix = enableHandleWeb ? this.matchingPrefixes.find((item)=>'/' === item || context.req.path === item || context.req.path.startsWith(`${item}/`)) || this.prefix : middlewarePrefix;
|
|
116
|
+
const response = await dispatchEffectBffRequest(handler, context.req.raw, {
|
|
117
|
+
prefix,
|
|
118
|
+
env: context.env,
|
|
119
|
+
path: context.req.path,
|
|
120
|
+
method: context.req.method,
|
|
121
|
+
onError: (error)=>createEffectAdapterRuntimeErrorResponse(this.api, error, context)
|
|
122
|
+
});
|
|
123
|
+
if (404 === response.status && enableHandleWeb) return void await next();
|
|
124
|
+
return new Response(response.body, response);
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
globalMiddlewares.push(...middlewares);
|
|
128
|
+
};
|
|
129
|
+
this.dispose = async ()=>{
|
|
130
|
+
this.retired = true;
|
|
131
|
+
this.unregisterRuntimeDisposer?.();
|
|
132
|
+
this.unregisterRuntimeDisposer = void 0;
|
|
133
|
+
const previous = this.handler;
|
|
134
|
+
this.handler = null;
|
|
135
|
+
await previous?.dispose();
|
|
136
|
+
};
|
|
137
|
+
this.api = api;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export { EffectAdapter };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import node_crypto from "node:crypto";
|
|
3
|
+
import node_fs from "node:fs";
|
|
4
|
+
import { builtinModules, createRequire } from "node:module";
|
|
5
|
+
import node_path from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { build, transform } from "esbuild";
|
|
8
|
+
const NODE_TARGET = 'node26.7';
|
|
9
|
+
const SOURCE_LOADERS = new Map([
|
|
10
|
+
[
|
|
11
|
+
'.js',
|
|
12
|
+
'js'
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
'.jsx',
|
|
16
|
+
'jsx'
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
'.mjs',
|
|
20
|
+
'js'
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
'.cjs',
|
|
24
|
+
'js'
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
'.ts',
|
|
28
|
+
'ts'
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
'.tsx',
|
|
32
|
+
'tsx'
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
'.mts',
|
|
36
|
+
'ts'
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
'.cts',
|
|
40
|
+
'ts'
|
|
41
|
+
]
|
|
42
|
+
]);
|
|
43
|
+
function sourceLoader(args) {
|
|
44
|
+
return SOURCE_LOADERS.get(node_path.extname(args.path).toLowerCase());
|
|
45
|
+
}
|
|
46
|
+
function preserveSourceModuleSemantics() {
|
|
47
|
+
return {
|
|
48
|
+
name: 'modern-js-effect-source-semantics',
|
|
49
|
+
setup (buildApi) {
|
|
50
|
+
buildApi.onLoad({
|
|
51
|
+
filter: /\.[cm]?[jt]sx?$/
|
|
52
|
+
}, async (args)=>{
|
|
53
|
+
const loader = sourceLoader(args);
|
|
54
|
+
if (!loader) return;
|
|
55
|
+
const source = await node_fs.promises.readFile(args.path, 'utf8');
|
|
56
|
+
const compiled = await transform(source, {
|
|
57
|
+
define: {
|
|
58
|
+
'import.meta.url': JSON.stringify(pathToFileURL(args.path).href)
|
|
59
|
+
},
|
|
60
|
+
jsx: 'automatic',
|
|
61
|
+
loader,
|
|
62
|
+
sourcefile: args.path,
|
|
63
|
+
target: NODE_TARGET
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
contents: compiled.code,
|
|
67
|
+
loader: 'js',
|
|
68
|
+
resolveDir: node_path.dirname(args.path)
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const nodeBuiltins = new Set([
|
|
75
|
+
...builtinModules,
|
|
76
|
+
...builtinModules.map((name)=>`node:${name}`)
|
|
77
|
+
]);
|
|
78
|
+
const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
|
|
79
|
+
async function isEsmOnlyFile(filename) {
|
|
80
|
+
const extension = node_path.extname(filename).toLowerCase();
|
|
81
|
+
if ('.mjs' === extension || '.mts' === extension) return true;
|
|
82
|
+
if ('.cjs' === extension || '.cts' === extension) return false;
|
|
83
|
+
let directory = node_path.dirname(filename);
|
|
84
|
+
while(true){
|
|
85
|
+
const packagePath = node_path.join(directory, 'package.json');
|
|
86
|
+
try {
|
|
87
|
+
const packageJson = JSON.parse(await node_fs.promises.readFile(packagePath, 'utf8'));
|
|
88
|
+
return 'module' === packageJson.type;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (!error || 'object' != typeof error || !('code' in error) || 'ENOENT' !== error.code) throw error;
|
|
91
|
+
}
|
|
92
|
+
const parent = node_path.dirname(directory);
|
|
93
|
+
if (parent === directory) return false;
|
|
94
|
+
directory = parent;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function isBareSpecifier(specifier) {
|
|
98
|
+
return !specifier.startsWith('.') && !specifier.startsWith('/') && !node_path.isAbsolute(specifier);
|
|
99
|
+
}
|
|
100
|
+
function externalizeInstalledDependencies(options) {
|
|
101
|
+
const runtimeRequire = createRequire(node_path.join(options.runtimeResolveDir, '__modern_effect_entry.cjs'));
|
|
102
|
+
return {
|
|
103
|
+
name: 'modern-js-effect-installed-dependencies',
|
|
104
|
+
setup (buildApi) {
|
|
105
|
+
buildApi.onResolve({
|
|
106
|
+
filter: /.*/
|
|
107
|
+
}, async (args)=>{
|
|
108
|
+
if (!isBareSpecifier(args.path) || nodeBuiltins.has(args.path) || args.pluginData?.[dependencyResolutionMarker] === true) return nodeBuiltins.has(args.path) ? {
|
|
109
|
+
external: true,
|
|
110
|
+
path: args.path
|
|
111
|
+
} : void 0;
|
|
112
|
+
const resolution = await buildApi.resolve(args.path, {
|
|
113
|
+
importer: args.importer,
|
|
114
|
+
kind: args.kind,
|
|
115
|
+
namespace: args.namespace,
|
|
116
|
+
pluginData: {
|
|
117
|
+
...args.pluginData,
|
|
118
|
+
[dependencyResolutionMarker]: true
|
|
119
|
+
},
|
|
120
|
+
resolveDir: args.resolveDir
|
|
121
|
+
});
|
|
122
|
+
if (resolution.errors.length > 0) return {
|
|
123
|
+
external: true,
|
|
124
|
+
path: args.path
|
|
125
|
+
};
|
|
126
|
+
if (resolution.external || !resolution.path) return {
|
|
127
|
+
external: true,
|
|
128
|
+
path: args.path
|
|
129
|
+
};
|
|
130
|
+
const realPath = await node_fs.promises.realpath(resolution.path).catch(()=>resolution.path);
|
|
131
|
+
if (realPath.includes(`${node_path.sep}node_modules${node_path.sep}`)) {
|
|
132
|
+
const runtimeResolution = await buildApi.resolve(args.path, {
|
|
133
|
+
importer: '',
|
|
134
|
+
kind: args.kind,
|
|
135
|
+
namespace: 'file',
|
|
136
|
+
pluginData: {
|
|
137
|
+
[dependencyResolutionMarker]: true
|
|
138
|
+
},
|
|
139
|
+
resolveDir: options.runtimeResolveDir
|
|
140
|
+
});
|
|
141
|
+
if (0 === runtimeResolution.errors.length && runtimeResolution.path) {
|
|
142
|
+
if ('cjs' === options.format) {
|
|
143
|
+
let requiredPath;
|
|
144
|
+
try {
|
|
145
|
+
requiredPath = runtimeRequire.resolve(args.path);
|
|
146
|
+
} catch {
|
|
147
|
+
requiredPath = runtimeResolution.path;
|
|
148
|
+
}
|
|
149
|
+
if (await isEsmOnlyFile(requiredPath)) return {
|
|
150
|
+
namespace: resolution.namespace,
|
|
151
|
+
path: resolution.path,
|
|
152
|
+
pluginData: resolution.pluginData,
|
|
153
|
+
sideEffects: resolution.sideEffects,
|
|
154
|
+
suffix: resolution.suffix,
|
|
155
|
+
warnings: resolution.warnings
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
external: true,
|
|
160
|
+
path: args.path
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
namespace: resolution.namespace,
|
|
166
|
+
path: resolution.path,
|
|
167
|
+
pluginData: resolution.pluginData,
|
|
168
|
+
sideEffects: resolution.sideEffects,
|
|
169
|
+
suffix: resolution.suffix,
|
|
170
|
+
warnings: resolution.warnings
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async function bundleEffectEntryForNode(options) {
|
|
177
|
+
await build({
|
|
178
|
+
absWorkingDir: options.appDir,
|
|
179
|
+
allowOverwrite: true,
|
|
180
|
+
bundle: true,
|
|
181
|
+
entryPoints: [
|
|
182
|
+
options.entryPath
|
|
183
|
+
],
|
|
184
|
+
format: options.format,
|
|
185
|
+
logLevel: 'silent',
|
|
186
|
+
outfile: options.entryPath,
|
|
187
|
+
platform: 'node',
|
|
188
|
+
plugins: [
|
|
189
|
+
externalizeInstalledDependencies({
|
|
190
|
+
format: options.format,
|
|
191
|
+
runtimeResolveDir: options.appDir
|
|
192
|
+
}),
|
|
193
|
+
preserveSourceModuleSemantics()
|
|
194
|
+
],
|
|
195
|
+
target: NODE_TARGET
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function isWatchableInput(filename) {
|
|
199
|
+
return !filename.includes(`${node_path.sep}node_modules${node_path.sep}`) && (SOURCE_LOADERS.has(node_path.extname(filename).toLowerCase()) || '.json' === node_path.extname(filename).toLowerCase());
|
|
200
|
+
}
|
|
201
|
+
function normalizeImportedModule(module) {
|
|
202
|
+
if (!module || 'object' != typeof module) return module;
|
|
203
|
+
const namespace = module;
|
|
204
|
+
const commonJsModule = namespace.default;
|
|
205
|
+
if (1 === Object.keys(namespace).length && null !== commonJsModule && 'object' == typeof commonJsModule && true === commonJsModule.__esModule) return commonJsModule;
|
|
206
|
+
return module;
|
|
207
|
+
}
|
|
208
|
+
function hasErrorCode(error, code) {
|
|
209
|
+
return null !== error && 'object' == typeof error && 'code' in error && error.code === code;
|
|
210
|
+
}
|
|
211
|
+
async function publishImmutableOutput(options) {
|
|
212
|
+
const revision = node_crypto.createHash('sha256').update(options.contents).digest('hex');
|
|
213
|
+
const outputPath = node_path.join(options.cacheDirectory, `${options.outputName}-${revision}.mjs`);
|
|
214
|
+
const temporaryPath = node_path.join(options.cacheDirectory, `.${options.outputName}-${node_crypto.randomUUID()}.tmp`);
|
|
215
|
+
try {
|
|
216
|
+
await node_fs.promises.writeFile(temporaryPath, options.contents, {
|
|
217
|
+
flag: 'wx'
|
|
218
|
+
});
|
|
219
|
+
try {
|
|
220
|
+
await node_fs.promises.link(temporaryPath, outputPath);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (!hasErrorCode(error, 'EEXIST')) throw error;
|
|
223
|
+
const publishedContents = await node_fs.promises.readFile(outputPath);
|
|
224
|
+
if (!publishedContents.equals(Buffer.from(options.contents))) throw new Error(`Effect source cache entry does not match its content address: ${outputPath}`);
|
|
225
|
+
}
|
|
226
|
+
} finally{
|
|
227
|
+
await node_fs.promises.rm(temporaryPath, {
|
|
228
|
+
force: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return outputPath;
|
|
232
|
+
}
|
|
233
|
+
async function loadEffectBuiltModule(resourcePath) {
|
|
234
|
+
const canonicalPath = await node_fs.promises.realpath(node_path.resolve(resourcePath));
|
|
235
|
+
if (!await isEsmOnlyFile(canonicalPath)) return createRequire(canonicalPath)(canonicalPath);
|
|
236
|
+
return normalizeImportedModule(await import(pathToFileURL(canonicalPath).href));
|
|
237
|
+
}
|
|
238
|
+
async function loadEffectSourceModule(options) {
|
|
239
|
+
const resourcePath = node_path.resolve(options.resourcePath);
|
|
240
|
+
const appDir = node_path.resolve(options.appDir ?? node_path.dirname(resourcePath));
|
|
241
|
+
const tsconfigPath = node_path.join(appDir, 'tsconfig.json');
|
|
242
|
+
const cacheDirectory = node_path.join(appDir, 'node_modules', '.cache', 'modern-js', 'effect-source-loader');
|
|
243
|
+
const outputName = node_crypto.createHash('sha256').update(resourcePath).digest('hex').slice(0, 24);
|
|
244
|
+
await node_fs.promises.mkdir(cacheDirectory, {
|
|
245
|
+
recursive: true
|
|
246
|
+
});
|
|
247
|
+
const result = await build({
|
|
248
|
+
absWorkingDir: appDir,
|
|
249
|
+
banner: {
|
|
250
|
+
js: `import { createRequire as __modernCreateEffectRequire } from 'node:module'; const require = __modernCreateEffectRequire(${JSON.stringify(pathToFileURL(resourcePath).href)});`
|
|
251
|
+
},
|
|
252
|
+
bundle: true,
|
|
253
|
+
entryPoints: [
|
|
254
|
+
resourcePath
|
|
255
|
+
],
|
|
256
|
+
format: 'esm',
|
|
257
|
+
jsx: 'automatic',
|
|
258
|
+
logLevel: 'silent',
|
|
259
|
+
metafile: true,
|
|
260
|
+
outfile: node_path.join(cacheDirectory, `${outputName}.mjs`),
|
|
261
|
+
platform: 'node',
|
|
262
|
+
plugins: [
|
|
263
|
+
externalizeInstalledDependencies({
|
|
264
|
+
format: 'esm',
|
|
265
|
+
runtimeResolveDir: appDir
|
|
266
|
+
}),
|
|
267
|
+
preserveSourceModuleSemantics()
|
|
268
|
+
],
|
|
269
|
+
sourcemap: 'inline',
|
|
270
|
+
target: NODE_TARGET,
|
|
271
|
+
tsconfig: node_fs.existsSync(tsconfigPath) ? tsconfigPath : void 0,
|
|
272
|
+
write: false
|
|
273
|
+
});
|
|
274
|
+
for (const input of Object.keys(result.metafile.inputs)){
|
|
275
|
+
const dependency = node_path.isAbsolute(input) ? node_path.normalize(input) : node_path.resolve(appDir, input);
|
|
276
|
+
if (isWatchableInput(dependency)) options.onDependency?.(dependency);
|
|
277
|
+
}
|
|
278
|
+
if (1 !== result.outputFiles.length) throw new Error(`Expected one Effect source output, received ${result.outputFiles.length}`);
|
|
279
|
+
const outputPath = await publishImmutableOutput({
|
|
280
|
+
cacheDirectory,
|
|
281
|
+
contents: result.outputFiles[0].contents,
|
|
282
|
+
outputName
|
|
283
|
+
});
|
|
284
|
+
return normalizeImportedModule(await import(pathToFileURL(outputPath).href));
|
|
285
|
+
}
|
|
286
|
+
export { bundleEffectEntryForNode, loadEffectBuiltModule, loadEffectSourceModule };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
async function generateEffectWorkerRuntimeWrapper(loader, options, resourcePath) {
|
|
3
|
+
const { generateEffectClient } = await import("../client-generator/generator.mjs");
|
|
4
|
+
const artifacts = await generateEffectClient({
|
|
5
|
+
appDir: options.appDir,
|
|
6
|
+
apiDir: options.apiDir,
|
|
7
|
+
resourcePath,
|
|
8
|
+
prefix: options.prefix,
|
|
9
|
+
port: Number(options.port),
|
|
10
|
+
target: 'bundle',
|
|
11
|
+
requestId: options.requestId,
|
|
12
|
+
requestCreator: options.requestCreator,
|
|
13
|
+
httpMethodDecider: options.httpMethodDecider,
|
|
14
|
+
dataPlatformBatch: options.effectDataPlatformBatch,
|
|
15
|
+
onDependency: (dependency)=>loader.addDependency(dependency)
|
|
16
|
+
});
|
|
17
|
+
const sourceRequest = `${resourcePath}?modern-bff-runtime-source`;
|
|
18
|
+
const operationContracts = artifacts?.operationContracts ?? {};
|
|
19
|
+
return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
|
|
20
|
+
import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge/dispatcher';
|
|
21
|
+
|
|
22
|
+
const __generatedOperationContracts = ${JSON.stringify(operationContracts)};
|
|
23
|
+
|
|
24
|
+
const __mergeGeneratedOperationContracts = policy => {
|
|
25
|
+
if (
|
|
26
|
+
!policy ||
|
|
27
|
+
!policy.expectedOperationContracts ||
|
|
28
|
+
typeof policy.expectedOperationContracts !== 'object' ||
|
|
29
|
+
Array.isArray(policy.expectedOperationContracts)
|
|
30
|
+
) {
|
|
31
|
+
return policy;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...policy,
|
|
35
|
+
expectedOperationContracts: {
|
|
36
|
+
...policy.expectedOperationContracts,
|
|
37
|
+
...__generatedOperationContracts,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const __modern_create_effect_bff_dispatcher = options =>
|
|
43
|
+
createEffectBffEdgeDispatcher({
|
|
44
|
+
...options,
|
|
45
|
+
crossProjectPolicy: __mergeGeneratedOperationContracts(
|
|
46
|
+
options?.crossProjectPolicy,
|
|
47
|
+
),
|
|
48
|
+
module: effectBffModule,
|
|
49
|
+
});
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
async function generateEffectClientCode(options) {
|
|
53
|
+
const { generateEffectClient } = await import("../client-generator/generator.mjs");
|
|
54
|
+
const artifacts = await generateEffectClient(options);
|
|
55
|
+
return null === artifacts ? null : artifacts.code;
|
|
56
|
+
}
|
|
57
|
+
export { generateEffectClientCode, generateEffectWorkerRuntimeWrapper };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { createSafeFailureResponse } from "@modern-js/runtime-extensions/safe-failure";
|
|
3
|
+
import { createHonoCrossProjectPolicyMiddleware } from "./cross-project-policy.mjs";
|
|
4
|
+
const bindHonoRouteHandlers = ({ handler, policy, routePath, onError, reportError })=>{
|
|
5
|
+
const handlers = Array.isArray(handler) ? handler : [
|
|
6
|
+
handler
|
|
7
|
+
];
|
|
8
|
+
const [firstHandler, ...restHandlers] = handlers;
|
|
9
|
+
let routeHandlers = handler;
|
|
10
|
+
if (firstHandler) {
|
|
11
|
+
const errorBoundary = async (context, next)=>{
|
|
12
|
+
try {
|
|
13
|
+
return await firstHandler(context, next);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
try {
|
|
16
|
+
if (onError) {
|
|
17
|
+
const result = await onError(error, context);
|
|
18
|
+
if (result instanceof Response) return result;
|
|
19
|
+
} else reportError?.(error);
|
|
20
|
+
} catch (configError) {
|
|
21
|
+
reportError?.(`Error in serverConfig.onError handler: ${configError}`);
|
|
22
|
+
}
|
|
23
|
+
return createSafeFailureResponse(error);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
routeHandlers = Array.isArray(handler) ? [
|
|
27
|
+
errorBoundary,
|
|
28
|
+
...restHandlers
|
|
29
|
+
] : errorBoundary;
|
|
30
|
+
}
|
|
31
|
+
if (!policy) return routeHandlers;
|
|
32
|
+
return [
|
|
33
|
+
createHonoCrossProjectPolicyMiddleware(policy, routePath),
|
|
34
|
+
...Array.isArray(routeHandlers) ? routeHandlers : [
|
|
35
|
+
routeHandlers
|
|
36
|
+
]
|
|
37
|
+
];
|
|
38
|
+
};
|
|
39
|
+
export { bindHonoRouteHandlers };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { checkCrossProjectPolicyResponse } from "../cross-project-policy/evaluation.mjs";
|
|
3
|
+
const createHonoCrossProjectPolicyMiddleware = (policy, routePath)=>async function(context, next) {
|
|
4
|
+
const denial = checkCrossProjectPolicyResponse(context.req.header(), policy, {
|
|
5
|
+
method: context.req.method,
|
|
6
|
+
routePath
|
|
7
|
+
});
|
|
8
|
+
if (denial) return denial;
|
|
9
|
+
return next();
|
|
10
|
+
};
|
|
11
|
+
export { createHonoCrossProjectPolicyMiddleware };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BackendFederationExpectedIdentity } from './identity';
|
|
2
|
+
import type { BackendFederationEntryExports } from './types';
|
|
3
|
+
export type BackendFederationEdgeLoadEntryPlugin = {
|
|
4
|
+
name: string;
|
|
5
|
+
loadEntry: (args: {
|
|
6
|
+
remoteInfo: BackendFederationEdgeRemote;
|
|
7
|
+
}) => BackendFederationEntryExports | undefined | Promise<BackendFederationEntryExports | undefined>;
|
|
8
|
+
};
|
|
9
|
+
export type BackendFederationEdgeLoadEntryPluginOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the fork-owned edge entry contract, not a raw Module Federation
|
|
12
|
+
* container. The edge runtime calls its optional `init` once per remote with
|
|
13
|
+
* `{ hostName }`, then caches each loaded expose.
|
|
14
|
+
*/
|
|
15
|
+
resolveEntry: (remote: BackendFederationEdgeRemote) => BackendFederationEntryExports | undefined | Promise<BackendFederationEntryExports | undefined>;
|
|
16
|
+
};
|
|
17
|
+
export type BackendFederationEdgeRemote = {
|
|
18
|
+
entry: `${'binding' | 'service' | 'static'}:${string}`;
|
|
19
|
+
entryGlobalName?: string;
|
|
20
|
+
expose?: string;
|
|
21
|
+
name: string;
|
|
22
|
+
shareScope?: string | string[];
|
|
23
|
+
type?: 'commonjs-module' | 'module' | string;
|
|
24
|
+
verification?: never;
|
|
25
|
+
};
|
|
26
|
+
type BackendFederationEdgeRuntimeBaseOptions = {
|
|
27
|
+
entryPolicy?: never;
|
|
28
|
+
hostName: string;
|
|
29
|
+
plugins?: BackendFederationEdgeLoadEntryPlugin[];
|
|
30
|
+
remote?: BackendFederationEdgeRemote;
|
|
31
|
+
remotes?: BackendFederationEdgeRemote[];
|
|
32
|
+
};
|
|
33
|
+
export type BackendFederationEdgeRuntimeOptions = BackendFederationEdgeRuntimeBaseOptions & {
|
|
34
|
+
expected: BackendFederationExpectedIdentity;
|
|
35
|
+
};
|
|
36
|
+
type EdgeRuntimeLoadOptions = BackendFederationEdgeRuntimeBaseOptions & {
|
|
37
|
+
allowMissingIdentityMetadata?: boolean;
|
|
38
|
+
expected?: BackendFederationExpectedIdentity;
|
|
39
|
+
};
|
|
40
|
+
export type BackendFederationEdgeRuntime = {
|
|
41
|
+
loadRemote<T>(request: string): Promise<T>;
|
|
42
|
+
};
|
|
43
|
+
export declare function createBackendFederationLoadEntryPlugin(options: BackendFederationEdgeLoadEntryPluginOptions): BackendFederationEdgeLoadEntryPlugin;
|
|
44
|
+
export declare function createBackendFederationRuntime(options: BackendFederationEdgeRuntimeOptions): BackendFederationEdgeRuntime;
|
|
45
|
+
export declare function createBackendFederationRuntimeForLoad(options: EdgeRuntimeLoadOptions): BackendFederationEdgeRuntime;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from '@modern-js/utils/universal';
|
|
2
|
+
import { type BackendFederationEdgeLoadEntryPlugin, type BackendFederationEdgeLoadEntryPluginOptions, type BackendFederationEdgeRemote, type BackendFederationEdgeRuntimeOptions } from './edge-runtime';
|
|
3
|
+
import type { BackendFederatedEffectApiModule, BackendFederationLoadOptions, BackendFederationIdentityLoadOptions as UniversalBackendFederationIdentityLoadOptions } from './types';
|
|
4
|
+
export { type BackendFederationEdgeLoadEntryPlugin, type BackendFederationEdgeLoadEntryPluginOptions, type BackendFederationEdgeRemote, type BackendFederationEdgeRuntime, type BackendFederationEdgeRuntimeOptions, createBackendFederationLoadEntryPlugin, createBackendFederationRuntime, } from './edge-runtime';
|
|
5
|
+
export { type BackendFederationExpectedIdentity, type BackendFederationIdentityIssue, validateExpectedBackendFederationIdentity, } from './identity';
|
|
6
|
+
export type { BackendFederatedEffectApiModule, BackendFederationEntryExports, } from './types';
|
|
7
|
+
export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, };
|
|
8
|
+
export type BackendFederationRemote = BackendFederationEdgeRemote;
|
|
9
|
+
export type BackendFederationRuntimeOptions = BackendFederationEdgeRuntimeOptions;
|
|
10
|
+
export type BackendFederationLoadEntryPluginOptions = BackendFederationEdgeLoadEntryPluginOptions;
|
|
11
|
+
export type EdgeBackendFederationLoadOptions = Omit<BackendFederationLoadOptions, 'entryPolicy' | 'plugins' | 'remote' | 'remotes' | 'runtime'> & {
|
|
12
|
+
entryPolicy?: never;
|
|
13
|
+
plugins?: BackendFederationEdgeLoadEntryPlugin[];
|
|
14
|
+
remote?: BackendFederationEdgeRemote;
|
|
15
|
+
remotes?: BackendFederationEdgeRemote[];
|
|
16
|
+
runtime?: never;
|
|
17
|
+
};
|
|
18
|
+
export type EdgeBackendFederationIdentityLoadOptions = Omit<UniversalBackendFederationIdentityLoadOptions, 'entryPolicy' | 'plugins' | 'remote' | 'remotes' | 'runtime'> & {
|
|
19
|
+
entryPolicy?: never;
|
|
20
|
+
plugins?: BackendFederationEdgeLoadEntryPlugin[];
|
|
21
|
+
remote?: BackendFederationEdgeRemote;
|
|
22
|
+
remotes?: BackendFederationEdgeRemote[];
|
|
23
|
+
runtime?: never;
|
|
24
|
+
};
|
|
25
|
+
export type BackendFederationIdentityLoadOptions = EdgeBackendFederationIdentityLoadOptions;
|
|
26
|
+
export declare function loadBackendFederatedEffectApi(options: EdgeBackendFederationIdentityLoadOptions): Promise<BackendFederatedEffectApiModule>;
|
|
27
|
+
/** @deprecated Pass `expected` delivery-unit identity. */
|
|
28
|
+
export declare function loadBackendFederatedEffectApi(options: EdgeBackendFederationLoadOptions): Promise<BackendFederatedEffectApiModule>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mandatory delivery-unit identity for public backend federation loads
|
|
3
|
+
* (MV-G23, ADR-0019 / RESOLUTION-0001 §2.3): consumers pass the record's
|
|
4
|
+
* `unitId` + `buildMarker` through to identity validation.
|
|
5
|
+
*/
|
|
6
|
+
export type BackendFederationExpectedIdentity = {
|
|
7
|
+
unitId: string;
|
|
8
|
+
buildMarker: string;
|
|
9
|
+
};
|
|
10
|
+
export type BackendFederationIdentityIssue = {
|
|
11
|
+
path: string;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Shared expected-identity validation for loaded backend federation modules.
|
|
16
|
+
* Compares the expose's `backendFederationContract.compatibility` identity
|
|
17
|
+
* (`unitId`, `build`) against the consumer's expected delivery-unit identity.
|
|
18
|
+
* Missing identity metadata is an error: with an expectation present there is
|
|
19
|
+
* no legacy escape hatch.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateExpectedBackendFederationIdentity(loaded: unknown, expected: BackendFederationExpectedIdentity, options?: {
|
|
22
|
+
/**
|
|
23
|
+
* Tolerate exposes that declare no identity metadata (legacy modules).
|
|
24
|
+
* Mismatching declared values still fail. Used by the manifest adapter,
|
|
25
|
+
* whose manifest-side identity is already validated against `expected`;
|
|
26
|
+
* the raw identity-aware loader is strict.
|
|
27
|
+
*/
|
|
28
|
+
allowMissingIdentityMetadata?: boolean;
|
|
29
|
+
}): BackendFederationIdentityIssue[];
|
|
30
|
+
export declare function formatBackendFederationIdentityIssues(issues: BackendFederationIdentityIssue[]): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from '@modern-js/utils/universal';
|
|
2
|
+
export { type BackendFederationExpectedIdentity, type BackendFederationIdentityIssue, validateExpectedBackendFederationIdentity, } from './identity';
|
|
3
|
+
export { createBackendFederationLoadEntryPlugin } from './plugin';
|
|
4
|
+
export { createBackendFederationRuntime } from './runtime';
|
|
5
|
+
export type { BackendFederatedEffectApiModule, BackendFederationEntryExports, BackendFederationIdentityLoadOptions, BackendFederationLoadEntryPluginOptions, BackendFederationRemote, BackendFederationRuntimeOptions, } from './types';
|
|
6
|
+
export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare 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.";
|