@bleedingdev/modern-js-plugin-bff 3.5.0-ultramodern.102 → 3.5.0-ultramodern.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/compress.js +1 -1
- package/dist/cjs/cli/generator.js +106 -108
- package/dist/cjs/cli/serverGlobalVars.js +43 -57
- package/dist/cjs/loader.js +1 -1
- package/dist/cjs/runtime/effect/adapter/cross-project-policy.js +17 -12
- package/dist/cjs/runtime/effect/adapter/error-response.js +11 -9
- package/dist/cjs/runtime/effect/adapter.js +2 -2
- package/dist/cjs/runtime/effect/backend-federation/load.js +26 -17
- package/dist/cjs/runtime/effect/backend-federation/plugin.js +12 -2
- package/dist/cjs/runtime/effect/backend-federation/remotes.js +5 -4
- package/dist/cjs/runtime/effect/backend-federation/runtime.js +80 -16
- package/dist/cjs/runtime/effect/edge-dispatcher.js +148 -0
- package/dist/cjs/runtime/effect/edge.js +19 -100
- package/dist/cjs/utils/effect-client-generator/generator.js +17 -16
- package/dist/cjs/utils/effect-client-generator/paths.js +4 -15
- package/dist/cjs/utils/effect-client-generator/rendering.js +8 -19
- package/dist/cjs/utils/effect-client-generator/runtime.js +15 -32
- package/dist/cjs/utils/effectSourceLoader.js +10 -6
- package/dist/esm/cli/compress.mjs +1 -1
- package/dist/esm/cli/generator.mjs +107 -108
- package/dist/esm/cli/serverGlobalVars.mjs +44 -48
- package/dist/esm/loader.mjs +1 -1
- package/dist/esm/runtime/effect/adapter/cross-project-policy.mjs +17 -12
- package/dist/esm/runtime/effect/adapter/error-response.mjs +11 -9
- package/dist/esm/runtime/effect/adapter.mjs +3 -3
- package/dist/esm/runtime/effect/backend-federation/load.mjs +26 -17
- package/dist/esm/runtime/effect/backend-federation/plugin.mjs +12 -2
- package/dist/esm/runtime/effect/backend-federation/remotes.mjs +5 -4
- package/dist/esm/runtime/effect/backend-federation/runtime.mjs +80 -16
- package/dist/esm/runtime/effect/edge-dispatcher.mjs +38 -0
- package/dist/esm/runtime/effect/edge.mjs +1 -38
- package/dist/esm/utils/effect-client-generator/generator.mjs +17 -16
- package/dist/esm/utils/effect-client-generator/paths.mjs +5 -6
- package/dist/esm/utils/effect-client-generator/rendering.mjs +9 -10
- package/dist/esm/utils/effect-client-generator/runtime.mjs +16 -23
- package/dist/esm/utils/effectSourceLoader.mjs +8 -7
- package/dist/esm-node/cli/compress.mjs +1 -1
- package/dist/esm-node/cli/generator.mjs +107 -108
- package/dist/esm-node/cli/serverGlobalVars.mjs +44 -48
- package/dist/esm-node/loader.mjs +1 -1
- package/dist/esm-node/runtime/effect/adapter/cross-project-policy.mjs +17 -12
- package/dist/esm-node/runtime/effect/adapter/error-response.mjs +11 -9
- package/dist/esm-node/runtime/effect/adapter.mjs +3 -3
- package/dist/esm-node/runtime/effect/backend-federation/load.mjs +26 -17
- package/dist/esm-node/runtime/effect/backend-federation/plugin.mjs +12 -2
- package/dist/esm-node/runtime/effect/backend-federation/remotes.mjs +5 -4
- package/dist/esm-node/runtime/effect/backend-federation/runtime.mjs +80 -16
- package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +39 -0
- package/dist/esm-node/runtime/effect/edge.mjs +1 -38
- package/dist/esm-node/utils/effect-client-generator/generator.mjs +17 -16
- package/dist/esm-node/utils/effect-client-generator/paths.mjs +5 -6
- package/dist/esm-node/utils/effect-client-generator/rendering.mjs +9 -10
- package/dist/esm-node/utils/effect-client-generator/runtime.mjs +16 -23
- package/dist/esm-node/utils/effectSourceLoader.mjs +8 -7
- package/dist/types/cli/generator.d.ts +2 -2
- package/dist/types/cli/serverGlobalVars.d.ts +4 -2
- package/dist/types/runtime/effect/adapter/cross-project-policy.d.ts +1 -1
- package/dist/types/runtime/effect/backend-federation/runtime.d.ts +1 -1
- package/dist/types/runtime/effect/edge-dispatcher.d.ts +26 -0
- package/dist/types/runtime/effect/edge.d.ts +1 -26
- package/dist/types/utils/effectSourceLoader.d.ts +10 -0
- package/package.json +34 -18
|
@@ -38,6 +38,7 @@ var __webpack_exports__ = {};
|
|
|
38
38
|
__webpack_require__.r(__webpack_exports__);
|
|
39
39
|
__webpack_require__.d(__webpack_exports__, {
|
|
40
40
|
bundleEffectEntryForNode: ()=>bundleEffectEntryForNode,
|
|
41
|
+
loadEffectBuiltModule: ()=>loadEffectBuiltModule,
|
|
41
42
|
loadEffectSourceModule: ()=>loadEffectSourceModule
|
|
42
43
|
});
|
|
43
44
|
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
@@ -196,9 +197,7 @@ function externalizeInstalledDependencies(options) {
|
|
|
196
197
|
pluginData: resolution.pluginData,
|
|
197
198
|
sideEffects: resolution.sideEffects,
|
|
198
199
|
suffix: resolution.suffix,
|
|
199
|
-
warnings: resolution.warnings
|
|
200
|
-
watchDirs: resolution.watchDirs,
|
|
201
|
-
watchFiles: resolution.watchFiles
|
|
200
|
+
warnings: resolution.warnings
|
|
202
201
|
};
|
|
203
202
|
}
|
|
204
203
|
return {
|
|
@@ -213,9 +212,7 @@ function externalizeInstalledDependencies(options) {
|
|
|
213
212
|
pluginData: resolution.pluginData,
|
|
214
213
|
sideEffects: resolution.sideEffects,
|
|
215
214
|
suffix: resolution.suffix,
|
|
216
|
-
warnings: resolution.warnings
|
|
217
|
-
watchDirs: resolution.watchDirs,
|
|
218
|
-
watchFiles: resolution.watchFiles
|
|
215
|
+
warnings: resolution.warnings
|
|
219
216
|
};
|
|
220
217
|
});
|
|
221
218
|
}
|
|
@@ -253,6 +250,11 @@ function normalizeImportedModule(module) {
|
|
|
253
250
|
if (1 === Object.keys(namespace).length && null !== commonJsModule && 'object' == typeof commonJsModule && true === commonJsModule.__esModule) return commonJsModule;
|
|
254
251
|
return module;
|
|
255
252
|
}
|
|
253
|
+
async function loadEffectBuiltModule(resourcePath) {
|
|
254
|
+
const canonicalPath = await external_node_fs_default().promises.realpath(external_node_path_default().resolve(resourcePath));
|
|
255
|
+
if (!await isEsmOnlyFile(canonicalPath)) return (0, external_node_module_namespaceObject.createRequire)(canonicalPath)(canonicalPath);
|
|
256
|
+
return normalizeImportedModule(await dynamicImport((0, external_node_url_namespaceObject.pathToFileURL)(canonicalPath).href));
|
|
257
|
+
}
|
|
256
258
|
async function loadEffectSourceModule(options) {
|
|
257
259
|
const resourcePath = external_node_path_default().resolve(options.resourcePath);
|
|
258
260
|
const appDir = external_node_path_default().resolve(options.appDir ?? external_node_path_default().dirname(resourcePath));
|
|
@@ -298,9 +300,11 @@ async function loadEffectSourceModule(options) {
|
|
|
298
300
|
return normalizeImportedModule(await dynamicImport(`${(0, external_node_url_namespaceObject.pathToFileURL)(outputPath).href}?v=${revision}`));
|
|
299
301
|
}
|
|
300
302
|
exports.bundleEffectEntryForNode = __webpack_exports__.bundleEffectEntryForNode;
|
|
303
|
+
exports.loadEffectBuiltModule = __webpack_exports__.loadEffectBuiltModule;
|
|
301
304
|
exports.loadEffectSourceModule = __webpack_exports__.loadEffectSourceModule;
|
|
302
305
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
303
306
|
"bundleEffectEntryForNode",
|
|
307
|
+
"loadEffectBuiltModule",
|
|
304
308
|
"loadEffectSourceModule"
|
|
305
309
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
306
310
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizePrefixList } from "./prefix.mjs";
|
|
2
2
|
const createCompressConfig = (devServer, prefix)=>{
|
|
3
|
-
if (
|
|
3
|
+
if (void 0 === devServer || 'object' != typeof devServer || Array.isArray(devServer)) return;
|
|
4
4
|
const { compress } = devServer;
|
|
5
5
|
if (void 0 === compress || true === compress) {
|
|
6
6
|
const prefixes = normalizePrefixList(prefix);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ApiRouter, buildOperationContractMap, deriveOperationVersion } from "@modern-js/bff-core";
|
|
2
2
|
import { compile } from "@modern-js/server-utils";
|
|
3
|
-
import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig } from "@modern-js/utils";
|
|
4
|
-
import path from "path";
|
|
3
|
+
import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig, upath } from "@modern-js/utils";
|
|
5
4
|
import clientGenerator from "../utils/clientGenerator.mjs";
|
|
6
5
|
import { bundleEffectEntryForNode } from "../utils/effectSourceLoader.mjs";
|
|
7
6
|
import pluginGenerator from "../utils/pluginGenerator.mjs";
|
|
@@ -20,121 +19,121 @@ const effectEntryExtensions = [
|
|
|
20
19
|
'.cts'
|
|
21
20
|
];
|
|
22
21
|
function resolveEffectSourceEntry(appDirectory, apiDirectory, configuredEntry) {
|
|
23
|
-
const entry = configuredEntry ?
|
|
24
|
-
return
|
|
22
|
+
const entry = void 0 !== configuredEntry && configuredEntry.length > 0 ? upath.isAbsolute(configuredEntry) ? configuredEntry : upath.resolve(appDirectory, configuredEntry) : upath.resolve(apiDirectory, 'index');
|
|
23
|
+
return upath.extname(entry).length > 0 ? entry : effectEntryExtensions.map((extension)=>`${entry}${extension}`).find((candidate)=>fs.existsSync(candidate));
|
|
25
24
|
}
|
|
26
25
|
function resolveBuiltEffectEntry(appDirectory, distDirectory, sourceEntry) {
|
|
27
|
-
if (
|
|
28
|
-
const relativeEntry =
|
|
29
|
-
if ('..' === relativeEntry || relativeEntry.startsWith(`..${
|
|
30
|
-
const builtEntry =
|
|
26
|
+
if (void 0 === sourceEntry || 0 === sourceEntry.length) return;
|
|
27
|
+
const relativeEntry = upath.relative(appDirectory, sourceEntry);
|
|
28
|
+
if ('..' === relativeEntry || relativeEntry.startsWith(`..${upath.sep}`)) throw new Error(`Effect BFF entry must be inside the application directory: ${sourceEntry}`);
|
|
29
|
+
const builtEntry = upath.resolve(distDirectory, relativeEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
|
|
31
30
|
return fs.existsSync(builtEntry) ? builtEntry : void 0;
|
|
32
31
|
}
|
|
33
32
|
const createBffGenerator = (api)=>{
|
|
34
|
-
const compileApi =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
alias
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
33
|
+
const compileApi = ()=>Promise.resolve().then(()=>{
|
|
34
|
+
const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
|
|
35
|
+
const modernConfig = api.getNormalizedConfig();
|
|
36
|
+
const distDir = upath.resolve(distDirectory);
|
|
37
|
+
const apiDir = apiDirectory || upath.resolve(appDirectory, API_DIR);
|
|
38
|
+
const sharedDir = sharedDirectory || upath.resolve(appDirectory, SHARED_DIR);
|
|
39
|
+
const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
|
|
40
|
+
const sourceDirs = [];
|
|
41
|
+
return fs.pathExists(apiDir).then((apiDirectoryExists)=>{
|
|
42
|
+
if (apiDirectoryExists) sourceDirs.push(apiDir);
|
|
43
|
+
return fs.pathExists(sharedDir);
|
|
44
|
+
}).then((sharedDirectoryExists)=>{
|
|
45
|
+
if (sharedDirectoryExists) sourceDirs.push(sharedDir);
|
|
46
|
+
if (0 === sourceDirs.length) return;
|
|
47
|
+
const { alias, globalVars } = modernConfig.source;
|
|
48
|
+
const { alias: resolveAlias } = modernConfig.resolve;
|
|
49
|
+
const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
|
|
50
|
+
const serializedGlobalVars = serializeServerGlobalVars(globalVars);
|
|
51
|
+
return compile(appDirectory, {
|
|
52
|
+
alias: combinedAlias
|
|
53
|
+
}, {
|
|
54
|
+
sourceDirs,
|
|
55
|
+
distDir,
|
|
56
|
+
tsconfigPath,
|
|
57
|
+
moduleType,
|
|
58
|
+
throwErrorInsteadOfExit: true
|
|
59
|
+
}).then(()=>transformServerGlobalVars(sourceDirs.map((sourceDir)=>upath.resolve(distDir, upath.relative(appDirectory, sourceDir))), serializedGlobalVars)).then(()=>{
|
|
60
|
+
if ('effect' !== bffRuntimeFramework) return;
|
|
61
|
+
const sourceEntry = resolveEffectSourceEntry(appDirectory, apiDir, modernConfig.bff?.effect?.entry);
|
|
62
|
+
const builtEntry = resolveBuiltEffectEntry(appDirectory, distDir, sourceEntry);
|
|
63
|
+
if (void 0 === builtEntry || 0 === builtEntry.length) throw new Error(`Effect BFF entry was not emitted into ${distDir}: ${sourceEntry ?? upath.resolve(apiDir, 'index')}`);
|
|
64
|
+
return bundleEffectEntryForNode({
|
|
65
|
+
appDir: appDirectory,
|
|
66
|
+
entryPath: builtEntry,
|
|
67
|
+
format: 'module' === moduleType ? 'esm' : 'cjs'
|
|
68
|
+
});
|
|
67
69
|
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const generate = async ()=>{
|
|
72
|
-
const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework } = api.getAppContext();
|
|
73
|
-
const modernConfig = api.getNormalizedConfig();
|
|
74
|
-
const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
|
|
75
|
-
const { bff } = modernConfig || {};
|
|
76
|
-
const prefix = getPrimaryPrefix(bff?.prefix);
|
|
77
|
-
const httpMethodDecider = bff?.httpMethodDecider;
|
|
78
|
-
const apiRouter = new ApiRouter({
|
|
79
|
-
apiDir: apiDirectory,
|
|
80
|
-
appDir: appDirectory,
|
|
81
|
-
lambdaDir: lambdaDirectory,
|
|
82
|
-
prefix,
|
|
83
|
-
httpMethodDecider,
|
|
84
|
-
isBuild: true
|
|
85
|
-
});
|
|
86
|
-
const lambdaDir = apiRouter.getLambdaDir();
|
|
87
|
-
const existLambda = apiRouter.isExistLambda();
|
|
88
|
-
const packageJson = fs.readJSONSync(path.resolve(appDirectory, 'package.json'));
|
|
89
|
-
const requestId = packageJson.name || process.env.npm_package_name;
|
|
90
|
-
const operationContracts = existLambda ? buildOperationContractMap({
|
|
91
|
-
handlers: await apiRouter.getApiHandlers(),
|
|
92
|
-
requestId,
|
|
93
|
-
operationVersion: deriveOperationVersion(packageJson.version)
|
|
94
|
-
}) : {};
|
|
95
|
-
const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
|
|
96
|
-
const relativeApiPath = path.relative(appDirectory, apiDirectory);
|
|
97
|
-
const relativeLambdaPath = path.relative(appDirectory, lambdaDir);
|
|
98
|
-
const sourceEffectEntry = 'effect' === bffRuntimeFramework ? resolveEffectSourceEntry(appDirectory, apiDirectory, bff?.effect?.entry) : void 0;
|
|
99
|
-
const relativeEffectEntry = sourceEffectEntry ? path.relative(appDirectory, sourceEffectEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js') : '';
|
|
100
|
-
await pluginGenerator({
|
|
101
|
-
prefix,
|
|
102
|
-
appDirectory,
|
|
103
|
-
relativeDistPath,
|
|
104
|
-
relativeApiPath,
|
|
105
|
-
relativeLambdaPath,
|
|
106
|
-
runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect',
|
|
107
|
-
relativeEffectEntry,
|
|
108
|
-
operationContracts
|
|
70
|
+
});
|
|
109
71
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
72
|
+
const generate = ()=>Promise.resolve().then(()=>{
|
|
73
|
+
const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework, packageName } = api.getAppContext();
|
|
74
|
+
const modernConfig = api.getNormalizedConfig();
|
|
75
|
+
const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
|
|
76
|
+
const { bff } = modernConfig || {};
|
|
77
|
+
const prefix = getPrimaryPrefix(bff?.prefix);
|
|
78
|
+
const httpMethodDecider = bff?.httpMethodDecider;
|
|
79
|
+
const apiRouter = new ApiRouter({
|
|
80
|
+
apiDir: apiDirectory,
|
|
81
|
+
appDir: appDirectory,
|
|
82
|
+
lambdaDir: lambdaDirectory,
|
|
83
|
+
prefix,
|
|
84
|
+
httpMethodDecider,
|
|
85
|
+
isBuild: true
|
|
86
|
+
});
|
|
87
|
+
const lambdaDir = apiRouter.getLambdaDir();
|
|
88
|
+
const existLambda = apiRouter.isExistLambda();
|
|
89
|
+
const packageJson = fs.readJSONSync(upath.resolve(appDirectory, 'package.json'));
|
|
90
|
+
const requestId = packageJson.name || packageName;
|
|
91
|
+
const operationContractsPromise = existLambda ? apiRouter.getApiHandlers().then((handlers)=>buildOperationContractMap({
|
|
92
|
+
handlers,
|
|
93
|
+
requestId,
|
|
94
|
+
operationVersion: deriveOperationVersion(packageJson.version)
|
|
95
|
+
})) : Promise.resolve({});
|
|
96
|
+
return operationContractsPromise.then((operationContracts)=>{
|
|
97
|
+
const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
|
|
98
|
+
const relativeApiPath = upath.relative(appDirectory, apiDirectory);
|
|
99
|
+
const relativeLambdaPath = upath.relative(appDirectory, lambdaDir);
|
|
100
|
+
const sourceEffectEntry = 'effect' === bffRuntimeFramework ? resolveEffectSourceEntry(appDirectory, apiDirectory, bff?.effect?.entry) : void 0;
|
|
101
|
+
const relativeEffectEntry = void 0 !== sourceEffectEntry && sourceEffectEntry.length > 0 ? upath.relative(appDirectory, sourceEffectEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js') : '';
|
|
102
|
+
return pluginGenerator({
|
|
103
|
+
prefix,
|
|
104
|
+
appDirectory,
|
|
105
|
+
relativeDistPath,
|
|
106
|
+
relativeApiPath,
|
|
107
|
+
relativeLambdaPath,
|
|
108
|
+
runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect',
|
|
109
|
+
relativeEffectEntry,
|
|
110
|
+
operationContracts
|
|
111
|
+
}).then(()=>clientGenerator({
|
|
112
|
+
prefix,
|
|
113
|
+
appDir: appDirectory,
|
|
114
|
+
apiDir: apiDirectory,
|
|
115
|
+
lambdaDir,
|
|
116
|
+
existLambda,
|
|
117
|
+
port,
|
|
118
|
+
requestCreator: bff?.requestCreator,
|
|
119
|
+
httpMethodDecider,
|
|
120
|
+
relativeDistPath,
|
|
121
|
+
relativeApiPath,
|
|
122
|
+
bffRuntimeFramework,
|
|
123
|
+
effectEntry: bff?.effect?.entry,
|
|
124
|
+
effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
|
|
125
|
+
})).then(()=>runtimeGenerator({
|
|
126
|
+
runtime,
|
|
127
|
+
appDirectory,
|
|
128
|
+
relativeDistPath
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
124
131
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
const handleCrossProjectInvocation = (isBuild = false)=>Promise.resolve().then(()=>{
|
|
133
|
+
const { bff } = api.getNormalizedConfig();
|
|
134
|
+
if (bff?.crossProject !== true) return;
|
|
135
|
+
return isBuild ? generate() : compileApi().then(()=>generate());
|
|
129
136
|
});
|
|
130
|
-
};
|
|
131
|
-
const handleCrossProjectInvocation = async (isBuild = false)=>{
|
|
132
|
-
const { bff } = api.getNormalizedConfig();
|
|
133
|
-
if (bff?.crossProject) {
|
|
134
|
-
if (!isBuild) await compileApi();
|
|
135
|
-
await generate();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
137
|
return {
|
|
139
138
|
compileApi,
|
|
140
139
|
generate,
|
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
import { applyOptionsChain, fs } from "@modern-js/utils";
|
|
1
|
+
import { applyOptionsChain, fs, upath } from "@modern-js/utils";
|
|
2
2
|
import { transform } from "@swc/core";
|
|
3
|
-
import path from "path";
|
|
4
3
|
const RELEASE_IDENTITY_GLOBAL_NAMES = [
|
|
5
4
|
'ULTRAMODERN_BUILD_MARKER',
|
|
6
5
|
'ULTRAMODERN_SOURCE_REVISION',
|
|
7
6
|
'ULTRAMODERN_RELEASE_VERSION'
|
|
8
7
|
];
|
|
9
|
-
const collectJavaScriptFiles =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
] : [];
|
|
19
|
-
}));
|
|
20
|
-
return nested.flat();
|
|
21
|
-
};
|
|
8
|
+
const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>fs.readdir(directory, {
|
|
9
|
+
withFileTypes: true
|
|
10
|
+
})).then((entries)=>Promise.all(entries.map((entry)=>{
|
|
11
|
+
const absolutePath = upath.join(directory, entry.name);
|
|
12
|
+
if (entry.isDirectory()) return collectJavaScriptFiles(absolutePath);
|
|
13
|
+
return /\.(?:c|m)?js$/u.test(entry.name) ? [
|
|
14
|
+
absolutePath
|
|
15
|
+
] : [];
|
|
16
|
+
})).then((nested)=>nested.flat()));
|
|
22
17
|
const serializeServerGlobalVars = (options)=>{
|
|
23
18
|
const globalVars = applyOptionsChain({}, options, {
|
|
24
19
|
env: 'server',
|
|
@@ -40,37 +35,38 @@ const serializeServerGlobalVars = (options)=>{
|
|
|
40
35
|
];
|
|
41
36
|
}));
|
|
42
37
|
};
|
|
43
|
-
const transformServerGlobalVars =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
};
|
|
38
|
+
const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
|
|
39
|
+
if (0 === Object.keys(globalVars).length) return;
|
|
40
|
+
return Promise.all(outputDirectories.map((directory)=>fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
|
|
41
|
+
const filesPromise = Promise.all(existingDirectories.filter((directory)=>void 0 !== directory).map(collectJavaScriptFiles)).then((files)=>files.flat());
|
|
42
|
+
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`void ${globalVars[name]};`).join('')}\n` : '';
|
|
43
|
+
return filesPromise.then((files)=>Promise.all(files.map((filename)=>fs.readFile(filename, 'utf8').then((source)=>{
|
|
44
|
+
const sourceMapFilename = `${filename}.map`;
|
|
45
|
+
return fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
|
|
46
|
+
const inputSourceMapPromise = sourceMapExists ? fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
|
|
47
|
+
return inputSourceMapPromise.then((inputSourceMap)=>transform(`${releaseIdentityBanner}${source}`, {
|
|
48
|
+
filename,
|
|
49
|
+
inputSourceMap,
|
|
50
|
+
sourceMaps: sourceMapExists,
|
|
51
|
+
jsc: {
|
|
52
|
+
parser: {
|
|
53
|
+
syntax: "ecmascript"
|
|
54
|
+
},
|
|
55
|
+
preserveAllComments: true,
|
|
56
|
+
target: 'es2022',
|
|
57
|
+
transform: {
|
|
58
|
+
optimizer: {
|
|
59
|
+
globals: {
|
|
60
|
+
vars: globalVars
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}).then((result)=>fs.writeFile(filename, result.code).then(()=>{
|
|
66
|
+
if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return fs.writeFile(sourceMapFilename, result.map);
|
|
67
|
+
})));
|
|
68
|
+
});
|
|
69
|
+
}))));
|
|
70
|
+
}).then(()=>void 0);
|
|
71
|
+
});
|
|
76
72
|
export { serializeServerGlobalVars, transformServerGlobalVars };
|
package/dist/esm/loader.mjs
CHANGED
|
@@ -26,7 +26,7 @@ async function transformEffectRuntimeSource(source, filename) {
|
|
|
26
26
|
function createEffectWorkerRuntimeWrapper(resourcePath) {
|
|
27
27
|
const sourceRequest = `${resourcePath}?${EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY}`;
|
|
28
28
|
return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
|
|
29
|
-
import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge';
|
|
29
|
+
import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge/dispatcher';
|
|
30
30
|
|
|
31
31
|
export const __modern_create_effect_bff_dispatcher = options =>
|
|
32
32
|
createEffectBffEdgeDispatcher({
|
|
@@ -2,22 +2,27 @@ import { logger } from "@modern-js/utils";
|
|
|
2
2
|
import { HttpApi } from "effect/unstable/httpapi";
|
|
3
3
|
import { resolveAdapterCrossProjectPolicy } from "../../../utils/crossProjectServerPolicy.mjs";
|
|
4
4
|
import { collectEffectEndpoints, extractHttpApiFromModule, toOperationContractSources } from "../endpoint-contracts.mjs";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
function resolveEffectAdapterCrossProjectPolicy(api, prefix, mod) {
|
|
6
|
+
const contractSourcesPromise = null === mod ? Promise.resolve([]) : Promise.resolve().then(()=>extractHttpApiFromModule(mod, HttpApi.isHttpApi)).then((effectApi)=>{
|
|
7
|
+
if (null !== effectApi) {
|
|
8
|
+
const reflect = (apiValue, handlers)=>{
|
|
9
|
+
if (!HttpApi.isHttpApi(apiValue)) throw new Error('[BFF][Effect] Endpoint reflection received a non-HttpApi value.');
|
|
10
|
+
HttpApi.reflect(apiValue, {
|
|
11
11
|
onGroup: handlers.onGroup ?? (()=>{}),
|
|
12
12
|
onEndpoint: handlers.onEndpoint
|
|
13
13
|
});
|
|
14
|
-
|
|
14
|
+
};
|
|
15
|
+
return toOperationContractSources(collectEffectEndpoints(reflect, effectApi, prefix));
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
return [];
|
|
18
|
+
}).catch((error)=>{
|
|
17
19
|
logger.warn(`[BFF][Effect] Failed reflect HttpApi endpoints cross-project policy: ${String(error)}`);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
return [];
|
|
21
|
+
});
|
|
22
|
+
return contractSourcesPromise.then((contractSources)=>{
|
|
23
|
+
const policy = resolveAdapterCrossProjectPolicy(api, contractSources);
|
|
24
|
+
if (policy?.enabled === true && 0 === contractSources.length) logger.warn('[BFF][Effect] Cross-project policy enabled but no HttpApi endpoints could be reflected; requests fail operation-contract matching unless allowUnknownOperations is enabled.');
|
|
25
|
+
return policy;
|
|
26
|
+
});
|
|
22
27
|
}
|
|
23
28
|
export { resolveEffectAdapterCrossProjectPolicy };
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { logger } from "@modern-js/utils";
|
|
2
2
|
import { createSafeFailureResponse } from "../../safe-failure.mjs";
|
|
3
|
-
|
|
3
|
+
function createEffectAdapterRuntimeErrorResponse(api, error, c) {
|
|
4
4
|
try {
|
|
5
5
|
const serverConfig = api.getServerConfig();
|
|
6
6
|
const onErrorHandler = serverConfig?.onError;
|
|
7
|
-
if (onErrorHandler) {
|
|
7
|
+
if (void 0 !== onErrorHandler) {
|
|
8
8
|
const onErrorContext = ensureJsonContext(c);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
return Promise.resolve(onErrorHandler(error instanceof Error ? error : new Error(String(error)), onErrorContext)).then((result)=>result instanceof Response ? result : createSafeFailureResponse(error)).catch((configError)=>{
|
|
10
|
+
logger.error(`Error in serverConfig.onError handler: ${String(configError)}`);
|
|
11
|
+
return createSafeFailureResponse(error);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
logger.error(error instanceof Error ? error : new Error(String(error)));
|
|
12
15
|
} catch (configError) {
|
|
13
16
|
logger.error(`Error in serverConfig.onError handler: ${configError}`);
|
|
14
17
|
}
|
|
15
|
-
return createSafeFailureResponse(error);
|
|
18
|
+
return Promise.resolve(createSafeFailureResponse(error));
|
|
16
19
|
}
|
|
17
20
|
function ensureJsonContext(c) {
|
|
18
|
-
|
|
19
|
-
if ('function' == typeof maybeJsonContext.json) return c;
|
|
21
|
+
if ('json' in c && 'function' == typeof c.json) return c;
|
|
20
22
|
const headers = {
|
|
21
23
|
'content-type': 'application/json; charset=utf-8'
|
|
22
24
|
};
|
|
@@ -27,7 +29,7 @@ function ensureJsonContext(c) {
|
|
|
27
29
|
headers: extraHeaders
|
|
28
30
|
} : statusOrInit;
|
|
29
31
|
const responseHeaders = new Headers(headers);
|
|
30
|
-
if (responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
|
|
32
|
+
if (void 0 !== responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
|
|
31
33
|
responseHeaders.set(key, value);
|
|
32
34
|
});
|
|
33
35
|
return new Response(JSON.stringify(data), {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fs, isProd, logger } from "@modern-js/utils";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
|
|
3
|
+
import { loadEffectBuiltModule, loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
|
|
4
4
|
import { before } from "./adapter/constants.mjs";
|
|
5
5
|
import { resolveEffectAdapterCrossProjectPolicy } from "./adapter/cross-project-policy.mjs";
|
|
6
6
|
import { resolveEffectAdapterEntryFile } from "./adapter/entry.mjs";
|
|
@@ -29,10 +29,10 @@ class EffectAdapter {
|
|
|
29
29
|
let mod;
|
|
30
30
|
try {
|
|
31
31
|
const { appDirectory } = this.api.getServerContext();
|
|
32
|
-
mod = await loadEffectSourceModule({
|
|
32
|
+
mod = await (isProd() ? loadEffectBuiltModule(entryFile) : loadEffectSourceModule({
|
|
33
33
|
resourcePath: entryFile,
|
|
34
34
|
appDir: appDirectory || path.dirname(entryFile)
|
|
35
|
-
});
|
|
35
|
+
}));
|
|
36
36
|
} catch (error) {
|
|
37
37
|
logger.error(`[BFF][Effect] Failed to load Effect entry: ${entryFile}\n${String(error)}`);
|
|
38
38
|
this.handler = null;
|
|
@@ -2,27 +2,36 @@ import { BACKEND_FEDERATION_EFFECT_EXPOSE } from "@modern-js/utils/universal";
|
|
|
2
2
|
import { formatBackendFederationIdentityIssues, validateExpectedBackendFederationIdentity } from "./identity.mjs";
|
|
3
3
|
import { createBackendFederationRuntime } from "./runtime.mjs";
|
|
4
4
|
import { isRecord, normalizeExpose } from "./utils.mjs";
|
|
5
|
+
import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
|
|
6
|
+
import * as __rspack_external_effect_Logger_1691a01b from "effect/Logger";
|
|
5
7
|
const LEGACY_LOAD_WARNING = '[BFF][Effect] loadBackendFederatedEffectApi was called without an expected delivery-unit identity (expected.unitId + expected.buildMarker). Identity-less public backend loads are deprecated (ADR-0019/MV-G23) and will be rejected in a future major.';
|
|
6
|
-
|
|
8
|
+
const legacyWarningLogger = __rspack_external_effect_Logger_1691a01b.withLeveledConsole(__rspack_external_effect_Logger_1691a01b.formatLogFmt);
|
|
9
|
+
function warnLegacyLoad() {
|
|
10
|
+
__rspack_external_effect_Effect_41060786.runSync(__rspack_external_effect_Effect_41060786.provideService(__rspack_external_effect_Effect_41060786.logWarning(LEGACY_LOAD_WARNING), __rspack_external_effect_Logger_1691a01b.CurrentLoggers, new Set([
|
|
11
|
+
legacyWarningLogger
|
|
12
|
+
])));
|
|
13
|
+
}
|
|
14
|
+
function loadBackendFederatedEffectApi(options) {
|
|
7
15
|
const remoteName = options.remote?.name ?? options.remoteName;
|
|
8
|
-
if (
|
|
9
|
-
if (void 0 === options.expected)
|
|
16
|
+
if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
|
|
17
|
+
if (void 0 === options.expected) warnLegacyLoad();
|
|
10
18
|
const runtime = options.runtime ?? createBackendFederationRuntime(options);
|
|
11
19
|
const expose = options.expose ?? options.remote?.expose ?? BACKEND_FEDERATION_EFFECT_EXPOSE;
|
|
12
20
|
const remoteRequest = `${remoteName}/${normalizeExpose(expose)}`;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
return runtime.loadRemote(remoteRequest).then((loaded)=>{
|
|
22
|
+
if (!isRecord(loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must load an object module.`);
|
|
23
|
+
const backendContract = loaded.backendFederationContract;
|
|
24
|
+
if (!isRecord(backendContract) || true !== backendContract.strictEffectApproach) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (strictEffectApproach: true).`);
|
|
25
|
+
if ('effect' !== backendContract.runtimeFramework) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (runtimeFramework: "effect").`);
|
|
26
|
+
if ('string' == typeof backendContract.name && backendContract.name !== remoteName) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} metadata name mismatch: expected ${remoteName}, received ${backendContract.name}.`);
|
|
27
|
+
if (!('runtime' in loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose api and runtime.`);
|
|
28
|
+
if (void 0 !== options.expected) {
|
|
29
|
+
const issues = validateExpectedBackendFederationIdentity(loaded, options.expected, {
|
|
30
|
+
allowMissingIdentityMetadata: true === options.allowMissingIdentityMetadata
|
|
31
|
+
});
|
|
32
|
+
if (issues.length > 0) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} delivery-unit identity mismatch: ${formatBackendFederationIdentityIssues(issues)}.`);
|
|
33
|
+
}
|
|
34
|
+
return loaded;
|
|
35
|
+
});
|
|
27
36
|
}
|
|
28
37
|
export { loadBackendFederatedEffectApi };
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
function createBackendFederationLoadEntryPlugin(options) {
|
|
2
2
|
return {
|
|
3
3
|
name: 'modernjs-backend-federation-load-entry',
|
|
4
|
-
|
|
5
|
-
return options.resolveEntry({
|
|
4
|
+
loadEntry ({ remoteInfo }) {
|
|
5
|
+
return Promise.resolve(options.resolveEntry({
|
|
6
6
|
name: remoteInfo.name,
|
|
7
7
|
entry: remoteInfo.entry,
|
|
8
8
|
type: remoteInfo.type,
|
|
9
9
|
entryGlobalName: remoteInfo.entryGlobalName,
|
|
10
10
|
shareScope: remoteInfo.shareScope
|
|
11
|
+
})).then((entry)=>{
|
|
12
|
+
if (void 0 === entry) return;
|
|
13
|
+
return {
|
|
14
|
+
get (id) {
|
|
15
|
+
return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
|
|
16
|
+
},
|
|
17
|
+
init (...args) {
|
|
18
|
+
return entry.init?.(...args);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
11
21
|
});
|
|
12
22
|
}
|
|
13
23
|
};
|
|
@@ -2,10 +2,11 @@ function collectRemotes(options) {
|
|
|
2
2
|
const remotes = [
|
|
3
3
|
...options.remotes ?? []
|
|
4
4
|
];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const configuredRemote = options.remote;
|
|
6
|
+
if (void 0 !== configuredRemote) {
|
|
7
|
+
const existingIndex = remotes.findIndex((remote)=>remote.name === configuredRemote.name);
|
|
8
|
+
if (existingIndex >= 0) remotes[existingIndex] = configuredRemote;
|
|
9
|
+
else remotes.push(configuredRemote);
|
|
9
10
|
}
|
|
10
11
|
return remotes;
|
|
11
12
|
}
|