@bleedingdev/modern-js-plugin-bff-extensions 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20
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/README.md +25 -5
- package/dist/cjs/backend-federation/edge-runtime.js +3 -15
- package/dist/cjs/backend-federation/edge.js +9 -10
- package/dist/cjs/backend-federation/identity.js +26 -31
- package/dist/cjs/backend-federation/index.js +5 -5
- package/dist/cjs/backend-federation/integrity-plugin.js +112 -0
- package/dist/cjs/backend-federation/load.js +3 -13
- package/dist/cjs/backend-federation/node-shared.js +85 -0
- package/dist/cjs/backend-federation/node.js +6 -1
- package/dist/cjs/backend-federation/plugin.js +40 -21
- package/dist/cjs/backend-federation/runtime.js +16 -84
- package/dist/cjs/backend-federation/validate-loaded.js +2 -4
- package/dist/cjs/backend-federation-manifest/load.js +40 -12
- package/dist/cjs/backend-federation-manifest/metadata.js +3 -3
- package/dist/cjs/backend-federation-manifest/node.js +3 -1
- package/dist/cjs/backend-federation-manifest/remote.js +3 -9
- package/dist/cjs/backend-federation-manifest/validation.js +16 -12
- package/dist/cjs/cross-project-policy/evaluation.js +3 -3
- package/dist/cjs/cross-project-policy/node.js +11 -30
- package/dist/cjs/{backend-federation/legacy-warning.js → cross-project-policy/producer-runtime.js} +23 -5
- package/dist/cjs/{effect-adapter → effect-server}/cross-project-policy.js +5 -5
- package/dist/cjs/effect-server/entry.js +63 -0
- package/dist/cjs/{effect-adapter → effect-server}/error-response.js +4 -4
- package/dist/cjs/effect-server/index.js +199 -0
- package/dist/cjs/{client-generator → effect-source-loader}/built-entry.js +5 -6
- package/dist/cjs/{client-generator/runtime.js → effect-source-loader/http-api-runtime.js} +12 -20
- package/dist/cjs/effect-source-loader/index.js +18 -6
- package/dist/cjs/effect-source-loader/loader.js +17 -30
- package/dist/cjs/effect-source-loader/operation-contracts.js +60 -0
- package/dist/cjs/effect-source-loader/paths.js +90 -0
- package/dist/cjs/effect-source-loader/rspack-loader.js +73 -0
- package/dist/cjs/effect-source-loader/worker-runtime-source.js +71 -0
- package/dist/cjs/effect-source-loader/worker-runtime-wrapper.js +7 -50
- package/dist/cjs/hono/cross-project-policy.js +2 -1
- package/dist/cjs/{client-generator/paths.js → hono/node.js} +14 -25
- package/dist/esm/backend-federation/edge-runtime.mjs +2 -11
- package/dist/esm/backend-federation/edge.mjs +5 -6
- package/dist/esm/backend-federation/identity.mjs +21 -32
- package/dist/esm/backend-federation/index.mjs +1 -1
- package/dist/esm/backend-federation/integrity-plugin.mjs +68 -0
- package/dist/esm/backend-federation/load.mjs +2 -12
- package/dist/esm/backend-federation/node-shared.mjs +41 -0
- package/dist/esm/backend-federation/node.mjs +4 -2
- package/dist/esm/backend-federation/plugin.mjs +37 -18
- package/dist/esm/backend-federation/runtime.mjs +16 -84
- package/dist/esm/backend-federation/validate-loaded.mjs +2 -4
- package/dist/esm/backend-federation-manifest/load.mjs +40 -12
- package/dist/esm/backend-federation-manifest/metadata.mjs +1 -1
- package/dist/esm/backend-federation-manifest/node.mjs +3 -1
- package/dist/esm/backend-federation-manifest/remote.mjs +2 -8
- package/dist/esm/backend-federation-manifest/validation.mjs +15 -11
- package/dist/esm/cross-project-policy/evaluation.mjs +1 -1
- package/dist/esm/cross-project-policy/node.mjs +10 -19
- package/dist/esm/cross-project-policy/producer-runtime.mjs +20 -0
- package/dist/esm/{effect-adapter → effect-server}/cross-project-policy.mjs +3 -3
- package/dist/esm/effect-server/entry.mjs +15 -0
- package/dist/esm/{effect-adapter → effect-server}/error-response.mjs +2 -2
- package/dist/esm/effect-server/index.mjs +151 -0
- package/dist/esm/{client-generator → effect-source-loader}/built-entry.mjs +6 -7
- package/dist/esm/effect-source-loader/http-api-runtime.mjs +19 -0
- package/dist/esm/effect-source-loader/index.mjs +5 -2
- package/dist/esm/effect-source-loader/loader.mjs +17 -30
- package/dist/esm/effect-source-loader/operation-contracts.mjs +22 -0
- package/dist/esm/effect-source-loader/paths.mjs +43 -0
- package/dist/esm/effect-source-loader/rspack-loader.mjs +35 -0
- package/dist/esm/effect-source-loader/worker-runtime-source.mjs +23 -0
- package/dist/esm/effect-source-loader/worker-runtime-wrapper.mjs +5 -45
- package/dist/esm/hono/cross-project-policy.mjs +2 -1
- package/dist/esm-node/backend-federation/edge-runtime.mjs +2 -11
- package/dist/esm-node/backend-federation/edge.mjs +5 -6
- package/dist/esm-node/backend-federation/identity.mjs +21 -32
- package/dist/esm-node/backend-federation/index.mjs +1 -1
- package/dist/esm-node/backend-federation/integrity-plugin.mjs +69 -0
- package/dist/esm-node/backend-federation/load.mjs +2 -12
- package/dist/esm-node/backend-federation/node-shared.mjs +42 -0
- package/dist/esm-node/backend-federation/node.mjs +4 -2
- package/dist/esm-node/backend-federation/plugin.mjs +37 -18
- package/dist/esm-node/backend-federation/runtime.mjs +16 -84
- package/dist/esm-node/backend-federation/validate-loaded.mjs +2 -4
- package/dist/esm-node/backend-federation-manifest/load.mjs +40 -12
- package/dist/esm-node/backend-federation-manifest/metadata.mjs +1 -1
- package/dist/esm-node/backend-federation-manifest/node.mjs +3 -1
- package/dist/esm-node/backend-federation-manifest/remote.mjs +2 -8
- package/dist/esm-node/backend-federation-manifest/validation.mjs +15 -11
- package/dist/esm-node/cross-project-policy/evaluation.mjs +1 -1
- package/dist/esm-node/cross-project-policy/node.mjs +10 -19
- package/dist/esm-node/cross-project-policy/producer-runtime.mjs +21 -0
- package/dist/esm-node/{effect-adapter → effect-server}/cross-project-policy.mjs +3 -3
- package/dist/esm-node/effect-server/entry.mjs +16 -0
- package/dist/esm-node/{effect-adapter → effect-server}/error-response.mjs +2 -2
- package/dist/esm-node/effect-server/index.mjs +152 -0
- package/dist/esm-node/{client-generator → effect-source-loader}/built-entry.mjs +6 -7
- package/dist/esm-node/effect-source-loader/http-api-runtime.mjs +20 -0
- package/dist/esm-node/effect-source-loader/index.mjs +5 -2
- package/dist/esm-node/effect-source-loader/loader.mjs +17 -30
- package/dist/esm-node/effect-source-loader/operation-contracts.mjs +23 -0
- package/dist/esm-node/effect-source-loader/paths.mjs +44 -0
- package/dist/esm-node/effect-source-loader/rspack-loader.mjs +37 -0
- package/dist/esm-node/effect-source-loader/worker-runtime-source.mjs +24 -0
- package/dist/esm-node/effect-source-loader/worker-runtime-wrapper.mjs +5 -45
- package/dist/esm-node/hono/cross-project-policy.mjs +2 -1
- package/dist/esm-node/hono/node.mjs +14 -0
- package/dist/types/backend-federation/edge-runtime.d.ts +0 -5
- package/dist/types/backend-federation/edge.d.ts +5 -5
- package/dist/types/backend-federation/identity.d.ts +4 -16
- package/dist/types/backend-federation/index.d.ts +1 -1
- package/dist/types/backend-federation/integrity-plugin.d.ts +13 -0
- package/dist/types/backend-federation/load.d.ts +1 -7
- package/dist/types/backend-federation/node-shared.d.ts +20 -0
- package/dist/types/backend-federation/node.d.ts +4 -3
- package/dist/types/backend-federation/plugin.d.ts +4 -0
- package/dist/types/backend-federation/types.d.ts +3 -14
- package/dist/types/backend-federation/validate-loaded.d.ts +1 -2
- package/dist/types/backend-federation-manifest/metadata.d.ts +0 -6
- package/dist/types/backend-federation-manifest/types.d.ts +2 -1
- package/dist/types/cross-project-policy/evaluation.d.ts +1 -1
- package/dist/types/cross-project-policy/node.d.ts +3 -2
- package/dist/types/cross-project-policy/producer-runtime.d.ts +20 -0
- package/dist/types/effect-server/cross-project-policy.d.ts +5 -0
- package/dist/types/effect-server/entry.d.ts +3 -0
- package/dist/types/effect-server/error-response.d.ts +2 -0
- package/dist/types/effect-server/index.d.ts +4 -0
- package/dist/types/effect-source-loader/index.d.ts +5 -2
- package/dist/types/effect-source-loader/operation-contracts.d.ts +8 -0
- package/dist/types/effect-source-loader/paths.d.ts +23 -0
- package/dist/types/effect-source-loader/rspack-loader.d.ts +9 -0
- package/dist/types/effect-source-loader/worker-runtime-source.d.ts +4 -0
- package/dist/types/effect-source-loader/worker-runtime-wrapper.d.ts +4 -22
- package/dist/types/hono/node.d.ts +4 -0
- package/package.json +64 -36
- package/src/backend-federation/edge-runtime.ts +2 -13
- package/src/backend-federation/edge.ts +16 -21
- package/src/backend-federation/identity.ts +39 -65
- package/src/backend-federation/index.ts +1 -1
- package/src/backend-federation/integrity-plugin.ts +157 -0
- package/src/backend-federation/load.ts +7 -35
- package/src/backend-federation/node-shared.ts +76 -0
- package/src/backend-federation/node.ts +7 -14
- package/src/backend-federation/plugin.ts +73 -25
- package/src/backend-federation/runtime.ts +16 -90
- package/src/backend-federation/types.ts +3 -18
- package/src/backend-federation/validate-loaded.ts +2 -7
- package/src/backend-federation-manifest/load.ts +93 -38
- package/src/backend-federation-manifest/metadata.ts +1 -7
- package/src/backend-federation-manifest/node.ts +3 -2
- package/src/backend-federation-manifest/remote.ts +1 -7
- package/src/backend-federation-manifest/types.ts +2 -0
- package/src/backend-federation-manifest/validation.ts +29 -39
- package/src/cross-project-policy/evaluation.ts +1 -1
- package/src/cross-project-policy/node.ts +10 -41
- package/src/cross-project-policy/producer-runtime.ts +43 -0
- package/src/{effect-adapter → effect-server}/cross-project-policy.ts +8 -2
- package/src/effect-server/entry.ts +18 -0
- package/src/{effect-adapter → effect-server}/error-response.ts +1 -1
- package/src/effect-server/index.ts +242 -0
- package/src/{client-generator → effect-source-loader}/built-entry.ts +12 -12
- package/src/effect-source-loader/http-api-runtime.ts +39 -0
- package/src/effect-source-loader/index.ts +8 -5
- package/src/effect-source-loader/loader.ts +47 -52
- package/src/effect-source-loader/operation-contracts.ts +38 -0
- package/src/effect-source-loader/paths.ts +74 -0
- package/src/effect-source-loader/rspack-loader.ts +71 -0
- package/src/effect-source-loader/worker-runtime-source.ts +34 -0
- package/src/effect-source-loader/worker-runtime-wrapper.ts +5 -75
- package/src/hono/cross-project-policy.ts +2 -1
- package/src/hono/node.ts +24 -0
- package/dist/cjs/backend-federation/entry.js +0 -94
- package/dist/cjs/client-generator/generator.js +0 -73
- package/dist/cjs/client-generator/index.js +0 -54
- package/dist/cjs/client-generator/rendering.js +0 -253
- package/dist/cjs/client-generator/types.js +0 -18
- package/dist/cjs/effect-adapter/entry.js +0 -98
- package/dist/cjs/effect-adapter/index.js +0 -187
- package/dist/esm/backend-federation/entry.mjs +0 -56
- package/dist/esm/backend-federation/legacy-warning.mjs +0 -2
- package/dist/esm/client-generator/generator.mjs +0 -32
- package/dist/esm/client-generator/index.mjs +0 -3
- package/dist/esm/client-generator/paths.mjs +0 -24
- package/dist/esm/client-generator/rendering.mjs +0 -209
- package/dist/esm/client-generator/runtime.mjs +0 -27
- package/dist/esm/client-generator/types.mjs +0 -0
- package/dist/esm/effect-adapter/entry.mjs +0 -50
- package/dist/esm/effect-adapter/index.mjs +0 -139
- package/dist/esm-node/backend-federation/entry.mjs +0 -57
- package/dist/esm-node/backend-federation/legacy-warning.mjs +0 -3
- package/dist/esm-node/client-generator/generator.mjs +0 -33
- package/dist/esm-node/client-generator/index.mjs +0 -4
- package/dist/esm-node/client-generator/paths.mjs +0 -25
- package/dist/esm-node/client-generator/rendering.mjs +0 -210
- package/dist/esm-node/client-generator/runtime.mjs +0 -29
- package/dist/esm-node/client-generator/types.mjs +0 -1
- package/dist/esm-node/effect-adapter/entry.mjs +0 -51
- package/dist/esm-node/effect-adapter/index.mjs +0 -140
- package/dist/types/backend-federation/entry.d.ts +0 -2
- package/dist/types/backend-federation/legacy-warning.d.ts +0 -1
- package/dist/types/client-generator/generator.d.ts +0 -3
- package/dist/types/client-generator/index.d.ts +0 -4
- package/dist/types/client-generator/paths.d.ts +0 -5
- package/dist/types/client-generator/rendering.d.ts +0 -24
- package/dist/types/client-generator/types.d.ts +0 -33
- package/dist/types/effect-adapter/cross-project-policy.d.ts +0 -4
- package/dist/types/effect-adapter/entry.d.ts +0 -2
- package/dist/types/effect-adapter/error-response.d.ts +0 -2
- package/dist/types/effect-adapter/index.d.ts +0 -19
- package/src/backend-federation/entry.ts +0 -125
- package/src/backend-federation/legacy-warning.ts +0 -2
- package/src/client-generator/generator.ts +0 -52
- package/src/client-generator/index.ts +0 -10
- package/src/client-generator/paths.ts +0 -40
- package/src/client-generator/rendering.ts +0 -307
- package/src/client-generator/runtime.ts +0 -69
- package/src/client-generator/types.ts +0 -35
- package/src/effect-adapter/entry.ts +0 -95
- package/src/effect-adapter/index.ts +0 -246
- /package/dist/types/{client-generator → effect-source-loader}/built-entry.d.ts +0 -0
- /package/dist/types/{client-generator/runtime.d.ts → effect-source-loader/http-api-runtime.d.ts} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { extractHttpApiFromModule } from "@modern-js/bff-effect/effect";
|
|
2
|
+
import { HttpApi } from "effect/unstable/httpapi";
|
|
3
|
+
import { loadEffectSourceModule } from "./loader.mjs";
|
|
4
|
+
function getHttpApiRuntime() {
|
|
5
|
+
return Promise.resolve({
|
|
6
|
+
isHttpApi: HttpApi.isHttpApi,
|
|
7
|
+
reflect: (api, handlers)=>{
|
|
8
|
+
if (!HttpApi.isHttpApi(api)) throw new TypeError('[BFF][Effect] Expected an HttpApi contract.');
|
|
9
|
+
HttpApi.reflect(api, {
|
|
10
|
+
onGroup: handlers.onGroup ?? (()=>{}),
|
|
11
|
+
onEndpoint: handlers.onEndpoint
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function loadEffectApi(options) {
|
|
17
|
+
return getHttpApiRuntime().then((httpApiRuntime)=>loadEffectSourceModule(options).then((mod)=>extractHttpApiFromModule(mod, httpApiRuntime.isHttpApi)));
|
|
18
|
+
}
|
|
19
|
+
export { getHttpApiRuntime, loadEffectApi };
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { bundleBuiltEffectEntryForNode, resolveEffectEntryPaths } from "./built-entry.mjs";
|
|
2
2
|
export { bundleEffectEntryForNode } from "./loader.mjs";
|
|
3
|
-
export {
|
|
3
|
+
export { resolveEffectOperationContracts } from "./operation-contracts.mjs";
|
|
4
|
+
export { resolveEffectEntryFile } from "./paths.mjs";
|
|
5
|
+
export { bundleEffectWorkerRuntimeSource } from "./worker-runtime-source.mjs";
|
|
6
|
+
export { generateEffectWorkerRuntimeWrapper } from "./worker-runtime-wrapper.mjs";
|
|
@@ -47,16 +47,18 @@ function preserveSourceModuleSemantics() {
|
|
|
47
47
|
name: 'modern-js-effect-source-semantics',
|
|
48
48
|
setup (buildApi) {
|
|
49
49
|
buildApi.onLoad({
|
|
50
|
-
filter: /\.[cm]?[jt]sx
|
|
50
|
+
filter: /\.[cm]?[jt]sx?$/,
|
|
51
|
+
namespace: 'file'
|
|
51
52
|
}, async (args)=>{
|
|
52
53
|
const loader = sourceLoader(args);
|
|
53
54
|
if (!loader) return;
|
|
54
55
|
const source = await node_fs.promises.readFile(args.path, 'utf8');
|
|
56
|
+
const canonicalSourcePath = await node_fs.promises.realpath(args.path);
|
|
55
57
|
const compiled = await transform(source, {
|
|
56
58
|
define: {
|
|
57
|
-
__dirname: JSON.stringify(node_path.dirname(
|
|
58
|
-
__filename: JSON.stringify(
|
|
59
|
-
'import.meta.url': JSON.stringify(pathToFileURL(
|
|
59
|
+
__dirname: JSON.stringify(node_path.dirname(canonicalSourcePath)),
|
|
60
|
+
__filename: JSON.stringify(canonicalSourcePath),
|
|
61
|
+
'import.meta.url': JSON.stringify(pathToFileURL(canonicalSourcePath).href)
|
|
60
62
|
},
|
|
61
63
|
jsx: 'automatic',
|
|
62
64
|
loader,
|
|
@@ -77,6 +79,7 @@ const nodeBuiltins = new Set([
|
|
|
77
79
|
...builtinModules.map((name)=>`node:${name}`)
|
|
78
80
|
]);
|
|
79
81
|
const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
|
|
82
|
+
const BFF_RUNTIME_PACKAGE = /^@modern-js\/(?:bff-effect|plugin-bff)(?:\/|$)/;
|
|
80
83
|
async function isEsmOnlyFile(filename) {
|
|
81
84
|
const extension = node_path.extname(filename).toLowerCase();
|
|
82
85
|
if ('.mjs' === extension || '.mts' === extension) return true;
|
|
@@ -99,13 +102,16 @@ function isBareSpecifier(specifier) {
|
|
|
99
102
|
return !specifier.startsWith('.') && !specifier.startsWith('/') && !node_path.isAbsolute(specifier);
|
|
100
103
|
}
|
|
101
104
|
function externalizeInstalledDependencies(options) {
|
|
102
|
-
const runtimeRequire = createRequire(node_path.join(options.runtimeResolveDir, '__modern_effect_entry.cjs'));
|
|
103
105
|
return {
|
|
104
106
|
name: 'modern-js-effect-installed-dependencies',
|
|
105
107
|
setup (buildApi) {
|
|
106
108
|
buildApi.onResolve({
|
|
107
109
|
filter: /.*/
|
|
108
110
|
}, async (args)=>{
|
|
111
|
+
if (BFF_RUNTIME_PACKAGE.test(args.path)) return {
|
|
112
|
+
external: true,
|
|
113
|
+
path: args.path
|
|
114
|
+
};
|
|
109
115
|
if (!isBareSpecifier(args.path) || nodeBuiltins.has(args.path) || args.pluginData?.[dependencyResolutionMarker] === true) return nodeBuiltins.has(args.path) ? {
|
|
110
116
|
external: true,
|
|
111
117
|
path: args.path
|
|
@@ -129,7 +135,8 @@ function externalizeInstalledDependencies(options) {
|
|
|
129
135
|
path: args.path
|
|
130
136
|
};
|
|
131
137
|
const realPath = await node_fs.promises.realpath(resolution.path).catch(()=>resolution.path);
|
|
132
|
-
|
|
138
|
+
const loader = SOURCE_LOADERS.get(node_path.extname(realPath).toLowerCase());
|
|
139
|
+
if (realPath.includes(`${node_path.sep}node_modules${node_path.sep}`) && 'ts' !== loader && 'tsx' !== loader && 'jsx' !== loader) {
|
|
133
140
|
const runtimeResolution = await buildApi.resolve(args.path, {
|
|
134
141
|
importer: '',
|
|
135
142
|
kind: args.kind,
|
|
@@ -139,28 +146,10 @@ function externalizeInstalledDependencies(options) {
|
|
|
139
146
|
},
|
|
140
147
|
resolveDir: options.runtimeResolveDir
|
|
141
148
|
});
|
|
142
|
-
if (0 === runtimeResolution.errors.length && runtimeResolution.path) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
requiredPath = runtimeRequire.resolve(args.path);
|
|
147
|
-
} catch {
|
|
148
|
-
requiredPath = runtimeResolution.path;
|
|
149
|
-
}
|
|
150
|
-
if (await isEsmOnlyFile(requiredPath)) return {
|
|
151
|
-
namespace: resolution.namespace,
|
|
152
|
-
path: resolution.path,
|
|
153
|
-
pluginData: resolution.pluginData,
|
|
154
|
-
sideEffects: resolution.sideEffects,
|
|
155
|
-
suffix: resolution.suffix,
|
|
156
|
-
warnings: resolution.warnings
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
return {
|
|
160
|
-
external: true,
|
|
161
|
-
path: args.path
|
|
162
|
-
};
|
|
163
|
-
}
|
|
149
|
+
if (0 === runtimeResolution.errors.length && runtimeResolution.path) return {
|
|
150
|
+
external: true,
|
|
151
|
+
path: args.path
|
|
152
|
+
};
|
|
164
153
|
}
|
|
165
154
|
return {
|
|
166
155
|
namespace: resolution.namespace,
|
|
@@ -188,7 +177,6 @@ async function bundleEffectEntryForNode(options) {
|
|
|
188
177
|
platform: 'node',
|
|
189
178
|
plugins: [
|
|
190
179
|
externalizeInstalledDependencies({
|
|
191
|
-
format: options.format,
|
|
192
180
|
runtimeResolveDir: options.appDir
|
|
193
181
|
}),
|
|
194
182
|
preserveSourceModuleSemantics()
|
|
@@ -262,7 +250,6 @@ async function loadEffectSourceModule(options) {
|
|
|
262
250
|
platform: 'node',
|
|
263
251
|
plugins: [
|
|
264
252
|
externalizeInstalledDependencies({
|
|
265
|
-
format: 'esm',
|
|
266
253
|
runtimeResolveDir: appDir
|
|
267
254
|
}),
|
|
268
255
|
preserveSourceModuleSemantics()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { collectEffectEndpoints, toOperationContractSources } from "@modern-js/bff-effect/effect";
|
|
2
|
+
import { buildOperationContractMap, resolveOperationProducer } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
3
|
+
import { upath } from "@modern-js/utils";
|
|
4
|
+
import { getHttpApiRuntime, loadEffectApi } from "./http-api-runtime.mjs";
|
|
5
|
+
async function resolveEffectOperationContracts(options) {
|
|
6
|
+
const api = await loadEffectApi(options);
|
|
7
|
+
if (null === api) return null;
|
|
8
|
+
const runtime = await getHttpApiRuntime();
|
|
9
|
+
const endpoints = collectEffectEndpoints(runtime.reflect, api, options.prefix);
|
|
10
|
+
return buildOperationContractMap({
|
|
11
|
+
handlers: toOperationContractSources(endpoints),
|
|
12
|
+
...resolveOperationProducer({
|
|
13
|
+
directories: [
|
|
14
|
+
upath.dirname(options.resourcePath),
|
|
15
|
+
options.appDir
|
|
16
|
+
],
|
|
17
|
+
requestId: options.requestId,
|
|
18
|
+
onDependency: options.onDependency
|
|
19
|
+
})
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export { resolveEffectOperationContracts };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { findExists, fs, upath } from "@modern-js/utils";
|
|
2
|
+
const JS_OR_TS_EXTS = [
|
|
3
|
+
'.js',
|
|
4
|
+
'.jsx',
|
|
5
|
+
'.ts',
|
|
6
|
+
'.tsx',
|
|
7
|
+
'.mjs',
|
|
8
|
+
'.mts',
|
|
9
|
+
'.cjs',
|
|
10
|
+
'.cts'
|
|
11
|
+
];
|
|
12
|
+
const emittedEffectEntry = (entry)=>entry.replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
|
|
13
|
+
function relativeEffectAppPath(appDir, entry) {
|
|
14
|
+
const relative = upath.relative(appDir, entry);
|
|
15
|
+
return '..' === relative || relative.startsWith('../') || relative.split('/').includes('node_modules') ? void 0 : relative;
|
|
16
|
+
}
|
|
17
|
+
function existingEntry(entry) {
|
|
18
|
+
return upath.extname(entry) ? fs.existsSync(entry) ? entry : void 0 : findExists(JS_OR_TS_EXTS.map((ext)=>`${entry}${ext}`)) || void 0;
|
|
19
|
+
}
|
|
20
|
+
function resolveEffectEntry(options) {
|
|
21
|
+
const { appDir, apiDir, effectEntry, distDir } = options;
|
|
22
|
+
const entry = upath.resolve(appDir, effectEntry || upath.join(apiDir, 'index'));
|
|
23
|
+
if (distDir && void 0 !== relativeEffectAppPath(distDir, entry)) return {
|
|
24
|
+
kind: 'built-output',
|
|
25
|
+
path: existingEntry(emittedEffectEntry(entry))
|
|
26
|
+
};
|
|
27
|
+
const relative = relativeEffectAppPath(appDir, entry);
|
|
28
|
+
if (void 0 === relative) return {
|
|
29
|
+
kind: 'external-sdk',
|
|
30
|
+
path: existingEntry(entry)
|
|
31
|
+
};
|
|
32
|
+
return distDir ? {
|
|
33
|
+
kind: 'built-output',
|
|
34
|
+
path: existingEntry(emittedEffectEntry(upath.resolve(distDir, relative)))
|
|
35
|
+
} : {
|
|
36
|
+
kind: 'app-source',
|
|
37
|
+
path: existingEntry(entry)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolveEffectEntryFile(options) {
|
|
41
|
+
return resolveEffectEntry(options).path;
|
|
42
|
+
}
|
|
43
|
+
export { emittedEffectEntry, relativeEffectAppPath, resolveEffectEntry, resolveEffectEntryFile };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { bundleEffectWorkerRuntimeSource, generateEffectWorkerRuntimeWrapper, resolveEffectEntryFile } from "./index.mjs";
|
|
3
|
+
async function loader(source) {
|
|
4
|
+
this.cacheable();
|
|
5
|
+
delete require.cache[this.resourcePath];
|
|
6
|
+
const callback = this.async();
|
|
7
|
+
try {
|
|
8
|
+
const options = this.getOptions();
|
|
9
|
+
const entry = resolveEffectEntryFile(options);
|
|
10
|
+
if (!entry || realpathSync(entry) !== realpathSync(this.resourcePath)) throw new Error(`Unexpected Effect BFF loader resource: ${this.resourcePath}`);
|
|
11
|
+
const query = new URLSearchParams(this.resourceQuery);
|
|
12
|
+
if (query.has('modern-bff-runtime-source')) {
|
|
13
|
+
const { transform } = await import("@swc/core");
|
|
14
|
+
const transformed = await transform(source, {
|
|
15
|
+
filename: this.resourcePath,
|
|
16
|
+
sourceMaps: false,
|
|
17
|
+
jsc: {
|
|
18
|
+
parser: {
|
|
19
|
+
syntax: "typescript",
|
|
20
|
+
tsx: /\.[jt]sx$/.test(this.resourcePath)
|
|
21
|
+
},
|
|
22
|
+
target: 'es2024'
|
|
23
|
+
},
|
|
24
|
+
module: {
|
|
25
|
+
type: 'es6'
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
callback(void 0, await bundleEffectWorkerRuntimeSource(transformed.code, this.resourcePath, this));
|
|
29
|
+
} else if (query.has('modern-bff-runtime')) callback(void 0, await generateEffectWorkerRuntimeWrapper(this, options, this.resourcePath));
|
|
30
|
+
else throw new Error('Effect BFF entries are server-only. Import the shared HttpApi contract and use HttpApiClient.make (or makeEffectHttpApiClient) for a fully inferred client.');
|
|
31
|
+
} catch (error) {
|
|
32
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export default loader;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { build } from "esbuild";
|
|
3
|
+
async function bundleEffectWorkerRuntimeSource(source, resourcePath, loader) {
|
|
4
|
+
const result = await build({
|
|
5
|
+
absWorkingDir: node_path.dirname(resourcePath),
|
|
6
|
+
bundle: true,
|
|
7
|
+
format: 'esm',
|
|
8
|
+
metafile: true,
|
|
9
|
+
packages: 'external',
|
|
10
|
+
platform: 'neutral',
|
|
11
|
+
stdin: {
|
|
12
|
+
contents: source,
|
|
13
|
+
loader: 'js',
|
|
14
|
+
resolveDir: node_path.dirname(resourcePath),
|
|
15
|
+
sourcefile: resourcePath
|
|
16
|
+
},
|
|
17
|
+
target: 'es2024',
|
|
18
|
+
write: false
|
|
19
|
+
});
|
|
20
|
+
for (const dependency of Object.keys(result.metafile.inputs))if ('<stdin>' !== dependency) loader.addDependency(node_path.resolve(node_path.dirname(resourcePath), dependency));
|
|
21
|
+
return result.outputFiles[0].text;
|
|
22
|
+
}
|
|
23
|
+
export { bundleEffectWorkerRuntimeSource };
|
|
@@ -1,56 +1,16 @@
|
|
|
1
|
+
import { resolveEffectOperationContracts } from "./operation-contracts.mjs";
|
|
1
2
|
async function generateEffectWorkerRuntimeWrapper(loader, options, resourcePath) {
|
|
2
|
-
const
|
|
3
|
-
const artifacts = await generateEffectClient({
|
|
3
|
+
const operationContracts = await resolveEffectOperationContracts({
|
|
4
4
|
appDir: options.appDir,
|
|
5
|
-
apiDir: options.apiDir,
|
|
6
5
|
resourcePath,
|
|
7
6
|
prefix: options.prefix,
|
|
8
|
-
port: Number(options.port),
|
|
9
|
-
target: 'bundle',
|
|
10
7
|
requestId: options.requestId,
|
|
11
|
-
requestCreator: options.requestCreator,
|
|
12
|
-
httpMethodDecider: options.httpMethodDecider,
|
|
13
|
-
dataPlatformBatch: options.effectDataPlatformBatch,
|
|
14
8
|
onDependency: (dependency)=>loader.addDependency(dependency)
|
|
15
9
|
});
|
|
16
10
|
const sourceRequest = `${resourcePath}?modern-bff-runtime-source`;
|
|
17
|
-
const operationContracts = artifacts?.operationContracts ?? {};
|
|
18
11
|
return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
const __generatedOperationContracts = ${JSON.stringify(operationContracts)};
|
|
22
|
-
|
|
23
|
-
const __mergeGeneratedOperationContracts = policy => {
|
|
24
|
-
if (
|
|
25
|
-
!policy ||
|
|
26
|
-
!policy.expectedOperationContracts ||
|
|
27
|
-
typeof policy.expectedOperationContracts !== 'object' ||
|
|
28
|
-
Array.isArray(policy.expectedOperationContracts)
|
|
29
|
-
) {
|
|
30
|
-
return policy;
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
...policy,
|
|
34
|
-
expectedOperationContracts: {
|
|
35
|
-
...policy.expectedOperationContracts,
|
|
36
|
-
...__generatedOperationContracts,
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const __modern_create_effect_bff_dispatcher = options =>
|
|
42
|
-
createEffectBffEdgeDispatcher({
|
|
43
|
-
...options,
|
|
44
|
-
crossProjectPolicy: __mergeGeneratedOperationContracts(
|
|
45
|
-
options?.crossProjectPolicy,
|
|
46
|
-
),
|
|
47
|
-
module: effectBffModule,
|
|
48
|
-
});
|
|
12
|
+
import { createEffectBffEdgeDispatcherFactory } from '@modern-js/bff-effect/effect-edge';
|
|
13
|
+
export const __modern_create_effect_bff_dispatcher = createEffectBffEdgeDispatcherFactory(effectBffModule, ${JSON.stringify(operationContracts ?? {})});
|
|
49
14
|
`;
|
|
50
15
|
}
|
|
51
|
-
|
|
52
|
-
const { generateEffectClient } = await import("../client-generator/generator.mjs");
|
|
53
|
-
const artifacts = await generateEffectClient(options);
|
|
54
|
-
return null === artifacts ? null : artifacts.code;
|
|
55
|
-
}
|
|
56
|
-
export { generateEffectClientCode, generateEffectWorkerRuntimeWrapper };
|
|
16
|
+
export { generateEffectWorkerRuntimeWrapper };
|
|
@@ -5,6 +5,7 @@ const createHonoCrossProjectPolicyMiddleware = (policy, routePath)=>async functi
|
|
|
5
5
|
routePath
|
|
6
6
|
});
|
|
7
7
|
if (denial) return denial;
|
|
8
|
-
|
|
8
|
+
const result = await next();
|
|
9
|
+
return result instanceof Response ? result : void 0;
|
|
9
10
|
};
|
|
10
11
|
export { createHonoCrossProjectPolicyMiddleware };
|
|
@@ -67,7 +67,7 @@ async function resolveEdgeEntry(plugins, remote) {
|
|
|
67
67
|
}
|
|
68
68
|
throw new Error(`[BFF][Effect] Missing static or service-binding entry provider for backend federation remote ${remote.name}.`);
|
|
69
69
|
}
|
|
70
|
-
var _entries = /*#__PURE__*/ new WeakMap(), _expected = /*#__PURE__*/ new WeakMap(), _hostName = /*#__PURE__*/ new WeakMap(),
|
|
70
|
+
var _entries = /*#__PURE__*/ new WeakMap(), _expected = /*#__PURE__*/ new WeakMap(), _hostName = /*#__PURE__*/ new WeakMap(), _modules = /*#__PURE__*/ new WeakMap(), _plugins = /*#__PURE__*/ new WeakMap(), _remotes = /*#__PURE__*/ new WeakMap(), _loadEntry = /*#__PURE__*/ new WeakSet(), _loadModule = /*#__PURE__*/ new WeakSet();
|
|
71
71
|
class EdgeBackendFederationRuntime {
|
|
72
72
|
async loadRemote(request) {
|
|
73
73
|
const existing = _class_private_field_get(this, _modules).get(request);
|
|
@@ -94,10 +94,6 @@ class EdgeBackendFederationRuntime {
|
|
|
94
94
|
writable: true,
|
|
95
95
|
value: void 0
|
|
96
96
|
});
|
|
97
|
-
_class_private_field_init(this, _allowMissingIdentityMetadata, {
|
|
98
|
-
writable: true,
|
|
99
|
-
value: void 0
|
|
100
|
-
});
|
|
101
97
|
_class_private_field_init(this, _modules, {
|
|
102
98
|
writable: true,
|
|
103
99
|
value: void 0
|
|
@@ -113,7 +109,6 @@ class EdgeBackendFederationRuntime {
|
|
|
113
109
|
_class_private_field_set(this, _entries, new Map());
|
|
114
110
|
_class_private_field_set(this, _modules, new Map());
|
|
115
111
|
if (void 0 !== options.entryPolicy) throw new Error('[BFF][Effect] Edge backend federation does not execute entry evaluators. Register a static or service-binding entry provider.');
|
|
116
|
-
_class_private_field_set(this, _allowMissingIdentityMetadata, true === options.allowMissingIdentityMetadata);
|
|
117
112
|
_class_private_field_set(this, _expected, options.expected);
|
|
118
113
|
_class_private_field_set(this, _hostName, options.hostName);
|
|
119
114
|
_class_private_field_set(this, _plugins, options.plugins ?? []);
|
|
@@ -152,7 +147,6 @@ function loadModule(request) {
|
|
|
152
147
|
if ('function' != typeof factory) throw new Error(`[BFF][Effect] Backend federation expose ${remote.name}/${expose.replace(/^\.\//u, '')} must load factory function.`);
|
|
153
148
|
const loaded = await factory();
|
|
154
149
|
return validateLoadedBackendFederatedEffectApi(loaded, {
|
|
155
|
-
allowMissingIdentityMetadata: _class_private_field_get(this, _allowMissingIdentityMetadata),
|
|
156
150
|
expected: _class_private_field_get(this, _expected),
|
|
157
151
|
remoteName,
|
|
158
152
|
remoteRequest: request
|
|
@@ -181,7 +175,4 @@ function createBackendFederationRuntime(options) {
|
|
|
181
175
|
if (void 0 === options.expected || 'string' != typeof options.expected.unitId || 0 === options.expected.unitId.length || 'string' != typeof options.expected.buildMarker || 0 === options.expected.buildMarker.length) throw new Error('[BFF][Effect] Edge backend federation runtime requires expected.unitId and expected.buildMarker.');
|
|
182
176
|
return new EdgeBackendFederationRuntime(options);
|
|
183
177
|
}
|
|
184
|
-
|
|
185
|
-
return new EdgeBackendFederationRuntime(options);
|
|
186
|
-
}
|
|
187
|
-
export { createBackendFederationLoadEntryPlugin, createBackendFederationRuntime, createBackendFederationRuntimeForLoad };
|
|
178
|
+
export { createBackendFederationLoadEntryPlugin, createBackendFederationRuntime };
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from "@modern-js/
|
|
3
|
-
import { createBackendFederationLoadEntryPlugin, createBackendFederationRuntime
|
|
4
|
-
import { LEGACY_LOAD_WARNING } from "./legacy-warning.mjs";
|
|
2
|
+
import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from "@modern-js/backend-federation-contracts";
|
|
3
|
+
import { createBackendFederationLoadEntryPlugin, createBackendFederationRuntime } from "./edge-runtime.mjs";
|
|
5
4
|
import { normalizeExpose } from "./utils.mjs";
|
|
6
5
|
function loadBackendFederatedEffectApi(options) {
|
|
7
|
-
if (void 0 !== options.runtime || void 0 !== options.entryPolicy) return Promise.reject(new Error('[BFF][Effect] Edge backend federation does not execute custom runtimes or
|
|
6
|
+
if (void 0 !== options.runtime || void 0 !== options.entryPolicy || void 0 !== options.shared) return Promise.reject(new Error('[BFF][Effect] Edge backend federation does not execute custom runtimes, entry evaluators or host shares. Register a static or service-binding entry provider.'));
|
|
8
7
|
const remoteName = options.remote?.name ?? options.remoteName;
|
|
9
8
|
if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
|
|
10
|
-
if (
|
|
9
|
+
if (!options.expected?.unitId || !options.expected.buildMarker) return Promise.reject(new Error('[BFF][Effect] Backend federation requires expected.unitId and expected.buildMarker.'));
|
|
11
10
|
const expose = options.expose ?? options.remote?.expose ?? BACKEND_FEDERATION_EFFECT_EXPOSE;
|
|
12
11
|
const remoteRequest = `${remoteName}/${normalizeExpose(expose)}`;
|
|
13
12
|
let runtime;
|
|
14
13
|
try {
|
|
15
|
-
runtime =
|
|
14
|
+
runtime = createBackendFederationRuntime(options);
|
|
16
15
|
} catch (error) {
|
|
17
16
|
return Promise.reject(error);
|
|
18
17
|
}
|
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { isRecord } from "./utils.mjs";
|
|
3
|
-
function
|
|
4
|
-
const allowMissing = true === options.allowMissingIdentityMetadata;
|
|
5
|
-
const issues = [];
|
|
3
|
+
function backendFederationCompatibility(loaded) {
|
|
6
4
|
const contract = isRecord(loaded) ? loaded.backendFederationContract : void 0;
|
|
7
5
|
const compatibility = isRecord(contract) ? contract.compatibility : void 0;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
return isRecord(compatibility) ? compatibility : void 0;
|
|
7
|
+
}
|
|
8
|
+
function validateBackendFederationCompatibility(compatibility, expected) {
|
|
9
|
+
return Object.entries(expected).flatMap(([field, value])=>void 0 === value || 'string' == typeof compatibility[field] && compatibility[field].length > 0 && compatibility[field] === value ? [] : [
|
|
11
10
|
{
|
|
12
|
-
path:
|
|
13
|
-
message: '
|
|
11
|
+
path: `backendFederationContract.compatibility.${field}`,
|
|
12
|
+
message: `expected ${value}, received ${'string' == typeof compatibility[field] ? compatibility[field] : typeof compatibility[field]}`
|
|
14
13
|
}
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if ('string' != typeof build || 0 === build.length) {
|
|
29
|
-
if (!allowMissing) issues.push({
|
|
30
|
-
path: 'backendFederationContract.compatibility.build',
|
|
31
|
-
message: `missing build marker; expected ${expected.buildMarker}`
|
|
32
|
-
});
|
|
33
|
-
} else if (build !== expected.buildMarker) issues.push({
|
|
34
|
-
path: 'backendFederationContract.compatibility.build',
|
|
35
|
-
message: `expected ${expected.buildMarker}, received ${build}`
|
|
36
|
-
});
|
|
37
|
-
return issues;
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
16
|
+
function validateExpectedBackendFederationIdentity(loaded, expected) {
|
|
17
|
+
const compatibility = backendFederationCompatibility(loaded);
|
|
18
|
+
return compatibility ? validateBackendFederationCompatibility(compatibility, {
|
|
19
|
+
unitId: expected.unitId,
|
|
20
|
+
build: expected.buildMarker
|
|
21
|
+
}) : [
|
|
22
|
+
{
|
|
23
|
+
path: 'backendFederationContract.compatibility',
|
|
24
|
+
message: 'loaded module declares no compatibility metadata; delivery-unit identity cannot be validated'
|
|
25
|
+
}
|
|
26
|
+
];
|
|
38
27
|
}
|
|
39
28
|
function formatBackendFederationIdentityIssues(issues) {
|
|
40
29
|
return issues.map((issue)=>`${issue.path}: ${issue.message}`).join('; ');
|
|
41
30
|
}
|
|
42
|
-
export { formatBackendFederationIdentityIssues, validateExpectedBackendFederationIdentity };
|
|
31
|
+
export { backendFederationCompatibility, formatBackendFederationIdentityIssues, validateBackendFederationCompatibility, validateExpectedBackendFederationIdentity };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from "@modern-js/
|
|
2
|
+
export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION } from "@modern-js/backend-federation-contracts";
|
|
3
3
|
export { validateExpectedBackendFederationIdentity } from "./identity.mjs";
|
|
4
4
|
export { createBackendFederationLoadEntryPlugin } from "./plugin.mjs";
|
|
5
5
|
export { createBackendFederationRuntime } from "./runtime.mjs";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { evaluateBackendFederationCommonJsEntry, loadVerifiedBackendFederationEntry } from "@modern-js/server-runtime-extensions/backend-federation-security";
|
|
3
|
+
import { Module } from "@module-federation/runtime";
|
|
4
|
+
const PROVIDED_ENTRY_SCHEME = /^(?:binding|service|static):/u;
|
|
5
|
+
function decodeDataUrl(remote) {
|
|
6
|
+
const commaIndex = remote.entry.indexOf(',');
|
|
7
|
+
if (commaIndex < 0) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} has an invalid data URL entry.`);
|
|
8
|
+
return new TextEncoder().encode(decodeURIComponent(remote.entry.slice(commaIndex + 1)));
|
|
9
|
+
}
|
|
10
|
+
async function importContainer(remote) {
|
|
11
|
+
const namespace = await import(remote.entry);
|
|
12
|
+
return namespace.default ?? namespace;
|
|
13
|
+
}
|
|
14
|
+
function loadContainer(remote, options) {
|
|
15
|
+
const verified = Boolean(remote.verification || options.entryPolicy?.expected);
|
|
16
|
+
const scheme = /^[a-z][a-z0-9+.-]*:/iu.exec(remote.entry)?.[0].toLowerCase();
|
|
17
|
+
if ('https:' === scheme || 'http:' === scheme) {
|
|
18
|
+
if (!verified) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} requires verified entry bytes before network execution.`);
|
|
19
|
+
return loadVerifiedBackendFederationEntry({
|
|
20
|
+
...options.entryPolicy,
|
|
21
|
+
remote,
|
|
22
|
+
...remote.verification ? {
|
|
23
|
+
verification: remote.verification
|
|
24
|
+
} : {}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (verified) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} declares entry verification, but only http(s) entries are fetched and verified.`);
|
|
28
|
+
if ('data:' === scheme && 'module' !== remote.type) return Promise.resolve(evaluateBackendFederationCommonJsEntry(remote, decodeDataUrl(remote), options.entryPolicy?.evaluateCommonJs));
|
|
29
|
+
if ('data:' === scheme || 'file:' === scheme) return importContainer(remote);
|
|
30
|
+
throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} uses unsupported entry ${remote.entry}. Use a verified http(s) entry, a data: or file: entry, or a binding:, service: or static: entry served by createBackendFederationLoadEntryPlugin().`);
|
|
31
|
+
}
|
|
32
|
+
function registerRemoteContainer(origin, remoteInfo, container) {
|
|
33
|
+
const module = new Module({
|
|
34
|
+
host: origin,
|
|
35
|
+
remoteInfo
|
|
36
|
+
});
|
|
37
|
+
module.remoteEntryExports = container;
|
|
38
|
+
origin.moduleCache.set(remoteInfo.name, module);
|
|
39
|
+
}
|
|
40
|
+
function createBackendFederationIntegrityPlugin(options, remotes) {
|
|
41
|
+
const loading = new Map();
|
|
42
|
+
const register = async (origin, remoteInfo)=>{
|
|
43
|
+
const remote = remotes.find((candidate)=>candidate.name === remoteInfo.name);
|
|
44
|
+
if (void 0 === remote) throw new Error(`[BFF][Effect] Missing backend federation remote ${remoteInfo.name}.`);
|
|
45
|
+
if (PROVIDED_ENTRY_SCHEME.test(remote.entry)) {
|
|
46
|
+
if (origin.moduleCache.get(remote.name)?.remoteEntryExports === void 0) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} has no entry provider for ${remote.entry}. Pass createBackendFederationLoadEntryPlugin() in plugins.`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const container = await loadContainer(remote, options);
|
|
50
|
+
if ('function' != typeof container?.get) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} entry must expose get().`);
|
|
51
|
+
registerRemoteContainer(origin, remoteInfo, container);
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
name: 'modernjs-backend-federation-integrity',
|
|
55
|
+
afterMatchRemote ({ origin, remoteInfo }) {
|
|
56
|
+
if (void 0 === remoteInfo) return;
|
|
57
|
+
let pending = loading.get(remoteInfo.name);
|
|
58
|
+
if (void 0 === pending) {
|
|
59
|
+
pending = register(origin, remoteInfo);
|
|
60
|
+
loading.set(remoteInfo.name, pending);
|
|
61
|
+
pending.catch(()=>{
|
|
62
|
+
if (loading.get(remoteInfo.name) === pending) loading.delete(remoteInfo.name);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return pending;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export { PROVIDED_ENTRY_SCHEME, createBackendFederationIntegrityPlugin, registerRemoteContainer };
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { BACKEND_FEDERATION_EFFECT_EXPOSE } from "@modern-js/
|
|
3
|
-
import { LEGACY_LOAD_WARNING } from "./legacy-warning.mjs";
|
|
2
|
+
import { BACKEND_FEDERATION_EFFECT_EXPOSE } from "@modern-js/backend-federation-contracts";
|
|
4
3
|
import { collectRemotes } from "./remotes.mjs";
|
|
5
4
|
import { createBackendFederationRuntime } from "./runtime.mjs";
|
|
6
5
|
import { normalizeExpose } from "./utils.mjs";
|
|
7
6
|
import { validateLoadedBackendFederatedEffectApi } from "./validate-loaded.mjs";
|
|
8
|
-
import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
|
|
9
|
-
import * as __rspack_external_effect_Logger_1691a01b from "effect/Logger";
|
|
10
|
-
const legacyWarningLogger = __rspack_external_effect_Logger_1691a01b.withLeveledConsole(__rspack_external_effect_Logger_1691a01b.formatLogFmt);
|
|
11
|
-
function warnLegacyLoad() {
|
|
12
|
-
__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([
|
|
13
|
-
legacyWarningLogger
|
|
14
|
-
])));
|
|
15
|
-
}
|
|
16
7
|
function loadBackendFederatedEffectApi(options) {
|
|
17
8
|
const remoteName = options.remote?.name ?? options.remoteName;
|
|
18
9
|
if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
|
|
19
|
-
if (
|
|
10
|
+
if (!options.expected?.unitId || !options.expected.buildMarker) return Promise.reject(new Error('[BFF][Effect] Backend federation requires expected.unitId and expected.buildMarker.'));
|
|
20
11
|
if (void 0 !== options.runtime) {
|
|
21
12
|
const configuredRemote = collectRemotes(options).find((remote)=>remote.name === remoteName);
|
|
22
13
|
if (void 0 === configuredRemote || !/^(?:binding|service|static):/u.test(configuredRemote.entry)) return Promise.reject(new Error('[BFF][Effect] A custom Module Federation runtime requires a caller-pinned static or service-binding remote and cannot execute network backend federation entries.'));
|
|
@@ -26,7 +17,6 @@ function loadBackendFederatedEffectApi(options) {
|
|
|
26
17
|
const expose = options.expose ?? options.remote?.expose ?? BACKEND_FEDERATION_EFFECT_EXPOSE;
|
|
27
18
|
const remoteRequest = `${remoteName}/${normalizeExpose(expose)}`;
|
|
28
19
|
return runtime.loadRemote(remoteRequest).then((loaded)=>validateLoadedBackendFederatedEffectApi(loaded, {
|
|
29
|
-
allowMissingIdentityMetadata: options.allowMissingIdentityMetadata,
|
|
30
20
|
expected: options.expected,
|
|
31
21
|
remoteName,
|
|
32
22
|
remoteRequest
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
const fromAdapter = createRequire(import.meta.url);
|
|
4
|
+
const effectPackageJson = fromAdapter.resolve('@modern-js/bff-effect/package.json');
|
|
5
|
+
const fromEffectPackage = createRequire(effectPackageJson);
|
|
6
|
+
let hostShared;
|
|
7
|
+
function effectBffHostShared() {
|
|
8
|
+
if (void 0 !== hostShared) return hostShared;
|
|
9
|
+
const { EFFECT_BFF_CONTEXT_STORAGE_SHARE, EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE } = fromEffectPackage('@modern-js/bff-effect/effect-edge');
|
|
10
|
+
const { getEffectContextStorage } = fromEffectPackage('@modern-js/bff-effect/context');
|
|
11
|
+
const handlerFactoryRegistry = fromEffectPackage("#effect-entry-shape-registry");
|
|
12
|
+
const version = fromAdapter(effectPackageJson).version;
|
|
13
|
+
hostShared = {
|
|
14
|
+
[EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE]: {
|
|
15
|
+
version,
|
|
16
|
+
lib: ()=>handlerFactoryRegistry,
|
|
17
|
+
shareConfig: {
|
|
18
|
+
singleton: true,
|
|
19
|
+
requiredVersion: false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
[EFFECT_BFF_CONTEXT_STORAGE_SHARE]: {
|
|
23
|
+
version,
|
|
24
|
+
lib: getEffectContextStorage,
|
|
25
|
+
shareConfig: {
|
|
26
|
+
singleton: true,
|
|
27
|
+
requiredVersion: false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return hostShared;
|
|
32
|
+
}
|
|
33
|
+
function withEffectBffHostShared(shared) {
|
|
34
|
+
const host = effectBffHostShared();
|
|
35
|
+
const reserved = Object.keys(host).find((name)=>shared && name in shared);
|
|
36
|
+
if (void 0 !== reserved) throw new Error(`[BFF][Effect] Share ${reserved} is reserved for the Node host. Remove it from options.shared.`);
|
|
37
|
+
return {
|
|
38
|
+
...shared,
|
|
39
|
+
...host
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export { effectBffHostShared, withEffectBffHostShared };
|