@bleedingdev/modern-js-plugin-bff-build-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.10
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/client-artifacts.js +9 -5
- package/dist/cjs/compile.js +14 -1
- package/dist/cjs/hono-client-codegen.js +7 -17
- package/dist/cjs/index.js +15 -8
- package/dist/cjs/runtime-build-identity.js +18 -0
- package/dist/cjs/server-global-vars.js +6 -28
- package/dist/esm/client-artifacts.mjs +10 -6
- package/dist/esm/compile.mjs +14 -1
- package/dist/esm/hono-client-codegen.mjs +8 -8
- package/dist/esm/index.mjs +15 -8
- package/dist/esm/runtime-build-identity.mjs +0 -0
- package/dist/esm/server-global-vars.mjs +8 -27
- package/dist/esm-node/client-artifacts.mjs +10 -6
- package/dist/esm-node/compile.mjs +14 -1
- package/dist/esm-node/hono-client-codegen.mjs +9 -10
- package/dist/esm-node/index.mjs +15 -8
- package/dist/esm-node/runtime-build-identity.mjs +1 -0
- package/dist/esm-node/server-global-vars.mjs +8 -27
- package/dist/types/index.d.ts +1 -0
- package/dist/types/runtime-build-identity.d.ts +17 -0
- package/dist/types/server-global-vars.d.ts +2 -6
- package/package.json +32 -29
- package/src/client-artifacts.ts +6 -7
- package/src/compile.ts +36 -6
- package/src/hono-client-codegen.ts +6 -10
- package/src/index.ts +14 -10
- package/src/runtime-build-identity.ts +24 -0
- package/src/server-global-vars.ts +10 -47
|
@@ -33,7 +33,6 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
33
33
|
});
|
|
34
34
|
const bff_core_namespaceObject = require("@modern-js/bff-core");
|
|
35
35
|
const node_namespaceObject = require("@modern-js/server-runtime-extensions/bff-policy/node");
|
|
36
|
-
const utils_namespaceObject = require("@modern-js/utils");
|
|
37
36
|
const BFF_REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
38
37
|
function registerBffClientArtifacts(api, metadata) {
|
|
39
38
|
api.modifyBffClientArtifacts(async (context)=>{
|
|
@@ -51,7 +50,8 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
51
50
|
appDir: generation.appDirectory,
|
|
52
51
|
resourcePath: sourceEffectEntry,
|
|
53
52
|
prefix: generation.prefix,
|
|
54
|
-
requestId: generation.requestId
|
|
53
|
+
requestId: generation.requestId,
|
|
54
|
+
globalVars: config.source.globalVars
|
|
55
55
|
});
|
|
56
56
|
if (null === operationContracts) throw new Error(`Cannot resolve exported Effect HttpApi in ${sourceEffectEntry}.`);
|
|
57
57
|
metadata.set(generation, {
|
|
@@ -68,11 +68,15 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
68
68
|
httpMethodDecider: generation.httpMethodDecider,
|
|
69
69
|
isBuild: true
|
|
70
70
|
});
|
|
71
|
-
const packageJson = await utils_namespaceObject.fs.readJSON(utils_namespaceObject.upath.join(generation.appDirectory, 'package.json'));
|
|
72
71
|
const operationContracts = generation.existLambda ? (0, node_namespaceObject.buildOperationContractMap)({
|
|
73
72
|
handlers: await router.getApiHandlers(),
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
...(0, node_namespaceObject.resolveOperationProducer)({
|
|
74
|
+
directories: [
|
|
75
|
+
generation.apiDirectory,
|
|
76
|
+
generation.appDirectory
|
|
77
|
+
],
|
|
78
|
+
requestId: generation.requestId || 'default'
|
|
79
|
+
})
|
|
76
80
|
}) : {};
|
|
77
81
|
metadata.set(generation, {
|
|
78
82
|
runtimeFramework: 'hono',
|
package/dist/cjs/compile.js
CHANGED
|
@@ -30,16 +30,29 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
31
|
registerBffCompilation: ()=>registerBffCompilation
|
|
32
32
|
});
|
|
33
|
+
const server_global_vars_namespaceObject = require("@modern-js/plugin-bff-extensions/server-global-vars");
|
|
33
34
|
const external_server_global_vars_js_namespaceObject = require("./server-global-vars.js");
|
|
34
35
|
function registerBffCompilation(api) {
|
|
35
36
|
const serialized = new WeakMap();
|
|
36
37
|
api.onBeforeBffCompile((context)=>{
|
|
37
|
-
serialized.set(context, (0,
|
|
38
|
+
serialized.set(context, (0, server_global_vars_namespaceObject.serializeServerGlobalVars)(api.getNormalizedConfig().source.globalVars));
|
|
38
39
|
});
|
|
39
40
|
api.onAfterBffCompile(async (context)=>{
|
|
40
41
|
const globals = serialized.get(context);
|
|
41
42
|
if (!globals) throw new Error('BFF compilation completed without its before hook.');
|
|
42
43
|
serialized.delete(context);
|
|
44
|
+
const appContext = api.getAppContext();
|
|
45
|
+
const resolveIdentity = appContext.resolveBffRuntimeBuildIdentity;
|
|
46
|
+
if (true !== appContext.apiOnly && void 0 !== resolveIdentity) {
|
|
47
|
+
if (appContext.appDirectory !== context.appDirectory) throw new Error('BFF runtime build identity provider does not belong to this application.');
|
|
48
|
+
if ('function' != typeof resolveIdentity) throw new Error('BFF runtime build identity provider is invalid.');
|
|
49
|
+
const identity = await resolveIdentity(context);
|
|
50
|
+
if (null === identity || 'object' != typeof identity || !Object.isFrozen(identity)) throw new Error('BFF runtime build identity must be immutable.');
|
|
51
|
+
const { buildMarker, sourceRevision } = identity;
|
|
52
|
+
if ('string' != typeof buildMarker || !/^[0-9a-f]{64}$/u.test(buildMarker) || 'string' != typeof sourceRevision || 0 === sourceRevision.trim().length) throw new Error('BFF runtime build identity is not finalized.');
|
|
53
|
+
globals.ULTRAMODERN_BUILD_MARKER = JSON.stringify(buildMarker);
|
|
54
|
+
globals.ULTRAMODERN_SOURCE_REVISION = JSON.stringify(sourceRevision);
|
|
55
|
+
}
|
|
43
56
|
await (0, external_server_global_vars_js_namespaceObject.transformServerGlobalVars)([
|
|
44
57
|
...context.outputDirectories
|
|
45
58
|
], globals);
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
|
-
(()=>{
|
|
4
|
-
__webpack_require__.n = (module)=>{
|
|
5
|
-
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
-
__webpack_require__.d(getter, {
|
|
7
|
-
a: getter
|
|
8
|
-
});
|
|
9
|
-
return getter;
|
|
10
|
-
};
|
|
11
|
-
})();
|
|
12
3
|
(()=>{
|
|
13
4
|
__webpack_require__.d = (exports1, getters, values)=>{
|
|
14
5
|
var define = (defs, kind)=>{
|
|
@@ -39,19 +30,18 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
40
31
|
modifyClient: ()=>modifyClient
|
|
41
32
|
});
|
|
42
|
-
const external_node_path_namespaceObject = require("node:path");
|
|
43
|
-
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
44
33
|
const node_namespaceObject = require("@modern-js/server-runtime-extensions/bff-policy/node");
|
|
45
34
|
const REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
46
35
|
const modifyClient = (draft, context)=>{
|
|
47
36
|
const { appDir, requestId } = context.options;
|
|
48
37
|
draft.requestCreator = context.options.requestCreator || REQUEST_RUNTIME;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
const { operationVersion, requestId: normalizedRequestId } = (0, node_namespaceObject.resolveOperationProducer)({
|
|
39
|
+
directories: [
|
|
40
|
+
context.options.apiDir,
|
|
41
|
+
appDir
|
|
42
|
+
],
|
|
43
|
+
requestId: requestId || 'default'
|
|
44
|
+
});
|
|
55
45
|
const operationContracts = (0, node_namespaceObject.buildOperationContractMap)({
|
|
56
46
|
handlers: [
|
|
57
47
|
...context.handlerInfos
|
package/dist/cjs/index.js
CHANGED
|
@@ -70,19 +70,26 @@ const bffPlugin = ()=>({
|
|
|
70
70
|
const nativePlugins = plugins.filter((plugin)=>'@modern-js/plugin-bff/server-plugin' === plugin.name);
|
|
71
71
|
if (1 !== nativePlugins.length) throw new Error('Expected exactly one native BFF server plugin.');
|
|
72
72
|
const native = nativePlugins[0];
|
|
73
|
-
const runtimeAdapters = native.options?.runtimeAdapters ?? {};
|
|
74
|
-
const effect = '@modern-js/plugin-bff-extensions/effect-adapter';
|
|
75
73
|
const honoRouteBinder = '@modern-js/plugin-bff-extensions/hono/node';
|
|
76
74
|
if (native.options?.honoRouteBinder !== void 0 && native.options.honoRouteBinder !== honoRouteBinder) throw new Error('The Hono BFF route binder is already configured.');
|
|
77
|
-
if (
|
|
75
|
+
if ('hono' !== runtimeFramework) {
|
|
76
|
+
plugins.splice(plugins.indexOf(native), 1, {
|
|
77
|
+
name: '@modern-js/plugin-bff-extensions/effect-server'
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
plugins
|
|
81
|
+
};
|
|
82
|
+
}
|
|
78
83
|
native.options = {
|
|
79
84
|
...native.options,
|
|
80
|
-
honoRouteBinder
|
|
81
|
-
runtimeAdapters: {
|
|
82
|
-
...runtimeAdapters,
|
|
83
|
-
effect
|
|
84
|
-
}
|
|
85
|
+
honoRouteBinder
|
|
85
86
|
};
|
|
87
|
+
native.includeEntries = [
|
|
88
|
+
...new Set([
|
|
89
|
+
...native.includeEntries ?? [],
|
|
90
|
+
honoRouteBinder
|
|
91
|
+
])
|
|
92
|
+
];
|
|
86
93
|
return {
|
|
87
94
|
plugins
|
|
88
95
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.r = (exports1)=>{
|
|
5
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
6
|
+
value: 'Module'
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
var __webpack_exports__ = {};
|
|
14
|
+
__webpack_require__.r(__webpack_exports__);
|
|
15
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
16
|
+
Object.defineProperty(exports, '__esModule', {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
@@ -28,11 +28,9 @@ var __webpack_require__ = {};
|
|
|
28
28
|
var __webpack_exports__ = {};
|
|
29
29
|
__webpack_require__.r(__webpack_exports__);
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
-
serializeServerGlobalVars: ()=>serializeServerGlobalVars,
|
|
32
31
|
transformServerGlobalVars: ()=>transformServerGlobalVars
|
|
33
32
|
});
|
|
34
33
|
const utils_namespaceObject = require("@modern-js/utils");
|
|
35
|
-
const core_namespaceObject = require("@swc/core");
|
|
36
34
|
const RELEASE_IDENTITY_GLOBAL_NAMES = [
|
|
37
35
|
'ULTRAMODERN_BUILD_MARKER',
|
|
38
36
|
'ULTRAMODERN_SOURCE_REVISION',
|
|
@@ -47,37 +45,18 @@ const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>utils_nam
|
|
|
47
45
|
absolutePath
|
|
48
46
|
] : [];
|
|
49
47
|
})).then((nested)=>nested.flat()));
|
|
50
|
-
const serializeServerGlobalVars = (options)=>{
|
|
51
|
-
const globalVars = (0, utils_namespaceObject.applyOptionsChain)({}, options, {
|
|
52
|
-
env: 'server',
|
|
53
|
-
target: 'node'
|
|
54
|
-
});
|
|
55
|
-
return Object.fromEntries(Object.entries(globalVars).map(([key, value])=>{
|
|
56
|
-
let serialized;
|
|
57
|
-
try {
|
|
58
|
-
serialized = JSON.stringify(value);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`, {
|
|
61
|
-
cause: error
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
if (void 0 === serialized) throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`);
|
|
65
|
-
return [
|
|
66
|
-
key,
|
|
67
|
-
serialized
|
|
68
|
-
];
|
|
69
|
-
}));
|
|
70
|
-
};
|
|
71
48
|
const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
|
|
72
49
|
if (0 === Object.keys(globalVars).length) return;
|
|
73
50
|
return Promise.all(outputDirectories.map((directory)=>utils_namespaceObject.fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
|
|
74
51
|
const filesPromise = Promise.all(existingDirectories.filter((directory)=>void 0 !== directory).map(collectJavaScriptFiles)).then((files)=>files.flat());
|
|
75
|
-
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)
|
|
52
|
+
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`${globalVars[name]};`).join('')}\n` : '';
|
|
76
53
|
return filesPromise.then((files)=>Promise.all(files.map((filename)=>utils_namespaceObject.fs.readFile(filename, 'utf8').then((source)=>{
|
|
77
54
|
const sourceMapFilename = `${filename}.map`;
|
|
78
55
|
return utils_namespaceObject.fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
|
|
79
56
|
const inputSourceMapPromise = sourceMapExists ? utils_namespaceObject.fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
|
|
80
|
-
return inputSourceMapPromise.then((inputSourceMap)=>
|
|
57
|
+
return inputSourceMapPromise.then(async (inputSourceMap)=>{
|
|
58
|
+
const { transform } = await import("@swc/core");
|
|
59
|
+
return transform(`${releaseIdentityBanner}${source}`, {
|
|
81
60
|
filename,
|
|
82
61
|
inputSourceMap,
|
|
83
62
|
sourceMaps: sourceMapExists,
|
|
@@ -97,15 +76,14 @@ const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resol
|
|
|
97
76
|
}
|
|
98
77
|
}).then((result)=>utils_namespaceObject.fs.writeFile(filename, result.code).then(()=>{
|
|
99
78
|
if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return utils_namespaceObject.fs.writeFile(sourceMapFilename, result.map);
|
|
100
|
-
}))
|
|
79
|
+
}));
|
|
80
|
+
});
|
|
101
81
|
});
|
|
102
82
|
}))));
|
|
103
83
|
}).then(()=>void 0);
|
|
104
84
|
});
|
|
105
|
-
exports.serializeServerGlobalVars = __webpack_exports__.serializeServerGlobalVars;
|
|
106
85
|
exports.transformServerGlobalVars = __webpack_exports__.transformServerGlobalVars;
|
|
107
86
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
108
|
-
"serializeServerGlobalVars",
|
|
109
87
|
"transformServerGlobalVars"
|
|
110
88
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
111
89
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ApiRouter } from "@modern-js/bff-core";
|
|
2
|
-
import { buildOperationContractMap,
|
|
3
|
-
import { fs, upath } from "@modern-js/utils";
|
|
2
|
+
import { buildOperationContractMap, resolveOperationProducer } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
4
3
|
const BFF_REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
5
4
|
function registerBffClientArtifacts(api, metadata) {
|
|
6
5
|
api.modifyBffClientArtifacts(async (context)=>{
|
|
@@ -18,7 +17,8 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
18
17
|
appDir: generation.appDirectory,
|
|
19
18
|
resourcePath: sourceEffectEntry,
|
|
20
19
|
prefix: generation.prefix,
|
|
21
|
-
requestId: generation.requestId
|
|
20
|
+
requestId: generation.requestId,
|
|
21
|
+
globalVars: config.source.globalVars
|
|
22
22
|
});
|
|
23
23
|
if (null === operationContracts) throw new Error(`Cannot resolve exported Effect HttpApi in ${sourceEffectEntry}.`);
|
|
24
24
|
metadata.set(generation, {
|
|
@@ -35,11 +35,15 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
35
35
|
httpMethodDecider: generation.httpMethodDecider,
|
|
36
36
|
isBuild: true
|
|
37
37
|
});
|
|
38
|
-
const packageJson = await fs.readJSON(upath.join(generation.appDirectory, 'package.json'));
|
|
39
38
|
const operationContracts = generation.existLambda ? buildOperationContractMap({
|
|
40
39
|
handlers: await router.getApiHandlers(),
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
...resolveOperationProducer({
|
|
41
|
+
directories: [
|
|
42
|
+
generation.apiDirectory,
|
|
43
|
+
generation.appDirectory
|
|
44
|
+
],
|
|
45
|
+
requestId: generation.requestId || 'default'
|
|
46
|
+
})
|
|
43
47
|
}) : {};
|
|
44
48
|
metadata.set(generation, {
|
|
45
49
|
runtimeFramework: 'hono',
|
package/dist/esm/compile.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { serializeServerGlobalVars
|
|
1
|
+
import { serializeServerGlobalVars } from "@modern-js/plugin-bff-extensions/server-global-vars";
|
|
2
|
+
import { transformServerGlobalVars } from "./server-global-vars.mjs";
|
|
2
3
|
function registerBffCompilation(api) {
|
|
3
4
|
const serialized = new WeakMap();
|
|
4
5
|
api.onBeforeBffCompile((context)=>{
|
|
@@ -8,6 +9,18 @@ function registerBffCompilation(api) {
|
|
|
8
9
|
const globals = serialized.get(context);
|
|
9
10
|
if (!globals) throw new Error('BFF compilation completed without its before hook.');
|
|
10
11
|
serialized.delete(context);
|
|
12
|
+
const appContext = api.getAppContext();
|
|
13
|
+
const resolveIdentity = appContext.resolveBffRuntimeBuildIdentity;
|
|
14
|
+
if (true !== appContext.apiOnly && void 0 !== resolveIdentity) {
|
|
15
|
+
if (appContext.appDirectory !== context.appDirectory) throw new Error('BFF runtime build identity provider does not belong to this application.');
|
|
16
|
+
if ('function' != typeof resolveIdentity) throw new Error('BFF runtime build identity provider is invalid.');
|
|
17
|
+
const identity = await resolveIdentity(context);
|
|
18
|
+
if (null === identity || 'object' != typeof identity || !Object.isFrozen(identity)) throw new Error('BFF runtime build identity must be immutable.');
|
|
19
|
+
const { buildMarker, sourceRevision } = identity;
|
|
20
|
+
if ('string' != typeof buildMarker || !/^[0-9a-f]{64}$/u.test(buildMarker) || 'string' != typeof sourceRevision || 0 === sourceRevision.trim().length) throw new Error('BFF runtime build identity is not finalized.');
|
|
21
|
+
globals.ULTRAMODERN_BUILD_MARKER = JSON.stringify(buildMarker);
|
|
22
|
+
globals.ULTRAMODERN_SOURCE_REVISION = JSON.stringify(sourceRevision);
|
|
23
|
+
}
|
|
11
24
|
await transformServerGlobalVars([
|
|
12
25
|
...context.outputDirectories
|
|
13
26
|
], globals);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { buildOperationContractMap, createOperationSchemaHash, deriveOperationVersion } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
1
|
+
import { buildOperationContractMap, createOperationSchemaHash, resolveOperationProducer } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
3
2
|
const REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
4
3
|
const modifyClient = (draft, context)=>{
|
|
5
4
|
const { appDir, requestId } = context.options;
|
|
6
5
|
draft.requestCreator = context.options.requestCreator || REQUEST_RUNTIME;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
const { operationVersion, requestId: normalizedRequestId } = resolveOperationProducer({
|
|
7
|
+
directories: [
|
|
8
|
+
context.options.apiDir,
|
|
9
|
+
appDir
|
|
10
|
+
],
|
|
11
|
+
requestId: requestId || 'default'
|
|
12
|
+
});
|
|
13
13
|
const operationContracts = buildOperationContractMap({
|
|
14
14
|
handlers: [
|
|
15
15
|
...context.handlerInfos
|
package/dist/esm/index.mjs
CHANGED
|
@@ -37,19 +37,26 @@ const src_bffPlugin = ()=>({
|
|
|
37
37
|
const nativePlugins = plugins.filter((plugin)=>'@modern-js/plugin-bff/server-plugin' === plugin.name);
|
|
38
38
|
if (1 !== nativePlugins.length) throw new Error('Expected exactly one native BFF server plugin.');
|
|
39
39
|
const native = nativePlugins[0];
|
|
40
|
-
const runtimeAdapters = native.options?.runtimeAdapters ?? {};
|
|
41
|
-
const effect = '@modern-js/plugin-bff-extensions/effect-adapter';
|
|
42
40
|
const honoRouteBinder = '@modern-js/plugin-bff-extensions/hono/node';
|
|
43
41
|
if (native.options?.honoRouteBinder !== void 0 && native.options.honoRouteBinder !== honoRouteBinder) throw new Error('The Hono BFF route binder is already configured.');
|
|
44
|
-
if (
|
|
42
|
+
if ('hono' !== runtimeFramework) {
|
|
43
|
+
plugins.splice(plugins.indexOf(native), 1, {
|
|
44
|
+
name: '@modern-js/plugin-bff-extensions/effect-server'
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
plugins
|
|
48
|
+
};
|
|
49
|
+
}
|
|
45
50
|
native.options = {
|
|
46
51
|
...native.options,
|
|
47
|
-
honoRouteBinder
|
|
48
|
-
runtimeAdapters: {
|
|
49
|
-
...runtimeAdapters,
|
|
50
|
-
effect
|
|
51
|
-
}
|
|
52
|
+
honoRouteBinder
|
|
52
53
|
};
|
|
54
|
+
native.includeEntries = [
|
|
55
|
+
...new Set([
|
|
56
|
+
...native.includeEntries ?? [],
|
|
57
|
+
honoRouteBinder
|
|
58
|
+
])
|
|
59
|
+
];
|
|
53
60
|
return {
|
|
54
61
|
plugins
|
|
55
62
|
};
|
|
File without changes
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { transform } from "@swc/core";
|
|
1
|
+
import { fs, upath } from "@modern-js/utils";
|
|
3
2
|
const RELEASE_IDENTITY_GLOBAL_NAMES = [
|
|
4
3
|
'ULTRAMODERN_BUILD_MARKER',
|
|
5
4
|
'ULTRAMODERN_SOURCE_REVISION',
|
|
@@ -14,37 +13,18 @@ const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>fs.readdi
|
|
|
14
13
|
absolutePath
|
|
15
14
|
] : [];
|
|
16
15
|
})).then((nested)=>nested.flat()));
|
|
17
|
-
const serializeServerGlobalVars = (options)=>{
|
|
18
|
-
const globalVars = applyOptionsChain({}, options, {
|
|
19
|
-
env: 'server',
|
|
20
|
-
target: 'node'
|
|
21
|
-
});
|
|
22
|
-
return Object.fromEntries(Object.entries(globalVars).map(([key, value])=>{
|
|
23
|
-
let serialized;
|
|
24
|
-
try {
|
|
25
|
-
serialized = JSON.stringify(value);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`, {
|
|
28
|
-
cause: error
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
if (void 0 === serialized) throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`);
|
|
32
|
-
return [
|
|
33
|
-
key,
|
|
34
|
-
serialized
|
|
35
|
-
];
|
|
36
|
-
}));
|
|
37
|
-
};
|
|
38
16
|
const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
|
|
39
17
|
if (0 === Object.keys(globalVars).length) return;
|
|
40
18
|
return Promise.all(outputDirectories.map((directory)=>fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
|
|
41
19
|
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)
|
|
20
|
+
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`${globalVars[name]};`).join('')}\n` : '';
|
|
43
21
|
return filesPromise.then((files)=>Promise.all(files.map((filename)=>fs.readFile(filename, 'utf8').then((source)=>{
|
|
44
22
|
const sourceMapFilename = `${filename}.map`;
|
|
45
23
|
return fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
|
|
46
24
|
const inputSourceMapPromise = sourceMapExists ? fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
|
|
47
|
-
return inputSourceMapPromise.then((inputSourceMap)=>
|
|
25
|
+
return inputSourceMapPromise.then(async (inputSourceMap)=>{
|
|
26
|
+
const { transform } = await import("@swc/core");
|
|
27
|
+
return transform(`${releaseIdentityBanner}${source}`, {
|
|
48
28
|
filename,
|
|
49
29
|
inputSourceMap,
|
|
50
30
|
sourceMaps: sourceMapExists,
|
|
@@ -64,9 +44,10 @@ const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resol
|
|
|
64
44
|
}
|
|
65
45
|
}).then((result)=>fs.writeFile(filename, result.code).then(()=>{
|
|
66
46
|
if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return fs.writeFile(sourceMapFilename, result.map);
|
|
67
|
-
}))
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
68
49
|
});
|
|
69
50
|
}))));
|
|
70
51
|
}).then(()=>void 0);
|
|
71
52
|
});
|
|
72
|
-
export {
|
|
53
|
+
export { transformServerGlobalVars };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { ApiRouter } from "@modern-js/bff-core";
|
|
3
|
-
import { buildOperationContractMap,
|
|
4
|
-
import { fs, upath } from "@modern-js/utils";
|
|
3
|
+
import { buildOperationContractMap, resolveOperationProducer } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
5
4
|
const BFF_REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
6
5
|
function registerBffClientArtifacts(api, metadata) {
|
|
7
6
|
api.modifyBffClientArtifacts(async (context)=>{
|
|
@@ -19,7 +18,8 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
19
18
|
appDir: generation.appDirectory,
|
|
20
19
|
resourcePath: sourceEffectEntry,
|
|
21
20
|
prefix: generation.prefix,
|
|
22
|
-
requestId: generation.requestId
|
|
21
|
+
requestId: generation.requestId,
|
|
22
|
+
globalVars: config.source.globalVars
|
|
23
23
|
});
|
|
24
24
|
if (null === operationContracts) throw new Error(`Cannot resolve exported Effect HttpApi in ${sourceEffectEntry}.`);
|
|
25
25
|
metadata.set(generation, {
|
|
@@ -36,11 +36,15 @@ function registerBffClientArtifacts(api, metadata) {
|
|
|
36
36
|
httpMethodDecider: generation.httpMethodDecider,
|
|
37
37
|
isBuild: true
|
|
38
38
|
});
|
|
39
|
-
const packageJson = await fs.readJSON(upath.join(generation.appDirectory, 'package.json'));
|
|
40
39
|
const operationContracts = generation.existLambda ? buildOperationContractMap({
|
|
41
40
|
handlers: await router.getApiHandlers(),
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
...resolveOperationProducer({
|
|
42
|
+
directories: [
|
|
43
|
+
generation.apiDirectory,
|
|
44
|
+
generation.appDirectory
|
|
45
|
+
],
|
|
46
|
+
requestId: generation.requestId || 'default'
|
|
47
|
+
})
|
|
44
48
|
}) : {};
|
|
45
49
|
metadata.set(generation, {
|
|
46
50
|
runtimeFramework: 'hono',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { serializeServerGlobalVars
|
|
2
|
+
import { serializeServerGlobalVars } from "@modern-js/plugin-bff-extensions/server-global-vars";
|
|
3
|
+
import { transformServerGlobalVars } from "./server-global-vars.mjs";
|
|
3
4
|
function registerBffCompilation(api) {
|
|
4
5
|
const serialized = new WeakMap();
|
|
5
6
|
api.onBeforeBffCompile((context)=>{
|
|
@@ -9,6 +10,18 @@ function registerBffCompilation(api) {
|
|
|
9
10
|
const globals = serialized.get(context);
|
|
10
11
|
if (!globals) throw new Error('BFF compilation completed without its before hook.');
|
|
11
12
|
serialized.delete(context);
|
|
13
|
+
const appContext = api.getAppContext();
|
|
14
|
+
const resolveIdentity = appContext.resolveBffRuntimeBuildIdentity;
|
|
15
|
+
if (true !== appContext.apiOnly && void 0 !== resolveIdentity) {
|
|
16
|
+
if (appContext.appDirectory !== context.appDirectory) throw new Error('BFF runtime build identity provider does not belong to this application.');
|
|
17
|
+
if ('function' != typeof resolveIdentity) throw new Error('BFF runtime build identity provider is invalid.');
|
|
18
|
+
const identity = await resolveIdentity(context);
|
|
19
|
+
if (null === identity || 'object' != typeof identity || !Object.isFrozen(identity)) throw new Error('BFF runtime build identity must be immutable.');
|
|
20
|
+
const { buildMarker, sourceRevision } = identity;
|
|
21
|
+
if ('string' != typeof buildMarker || !/^[0-9a-f]{64}$/u.test(buildMarker) || 'string' != typeof sourceRevision || 0 === sourceRevision.trim().length) throw new Error('BFF runtime build identity is not finalized.');
|
|
22
|
+
globals.ULTRAMODERN_BUILD_MARKER = JSON.stringify(buildMarker);
|
|
23
|
+
globals.ULTRAMODERN_SOURCE_REVISION = JSON.stringify(sourceRevision);
|
|
24
|
+
}
|
|
12
25
|
await transformServerGlobalVars([
|
|
13
26
|
...context.outputDirectories
|
|
14
27
|
], globals);
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import node_path from "node:path";
|
|
4
|
-
import { buildOperationContractMap, createOperationSchemaHash, deriveOperationVersion } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { buildOperationContractMap, createOperationSchemaHash, resolveOperationProducer } from "@modern-js/server-runtime-extensions/bff-policy/node";
|
|
5
3
|
const REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
6
4
|
const modifyClient = (draft, context)=>{
|
|
7
5
|
const { appDir, requestId } = context.options;
|
|
8
6
|
draft.requestCreator = context.options.requestCreator || REQUEST_RUNTIME;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
const { operationVersion, requestId: normalizedRequestId } = resolveOperationProducer({
|
|
8
|
+
directories: [
|
|
9
|
+
context.options.apiDir,
|
|
10
|
+
appDir
|
|
11
|
+
],
|
|
12
|
+
requestId: requestId || 'default'
|
|
13
|
+
});
|
|
15
14
|
const operationContracts = buildOperationContractMap({
|
|
16
15
|
handlers: [
|
|
17
16
|
...context.handlerInfos
|
package/dist/esm-node/index.mjs
CHANGED
|
@@ -39,19 +39,26 @@ const src_bffPlugin = ()=>({
|
|
|
39
39
|
const nativePlugins = plugins.filter((plugin)=>'@modern-js/plugin-bff/server-plugin' === plugin.name);
|
|
40
40
|
if (1 !== nativePlugins.length) throw new Error('Expected exactly one native BFF server plugin.');
|
|
41
41
|
const native = nativePlugins[0];
|
|
42
|
-
const runtimeAdapters = native.options?.runtimeAdapters ?? {};
|
|
43
|
-
const effect = '@modern-js/plugin-bff-extensions/effect-adapter';
|
|
44
42
|
const honoRouteBinder = '@modern-js/plugin-bff-extensions/hono/node';
|
|
45
43
|
if (native.options?.honoRouteBinder !== void 0 && native.options.honoRouteBinder !== honoRouteBinder) throw new Error('The Hono BFF route binder is already configured.');
|
|
46
|
-
if (
|
|
44
|
+
if ('hono' !== runtimeFramework) {
|
|
45
|
+
plugins.splice(plugins.indexOf(native), 1, {
|
|
46
|
+
name: '@modern-js/plugin-bff-extensions/effect-server'
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
plugins
|
|
50
|
+
};
|
|
51
|
+
}
|
|
47
52
|
native.options = {
|
|
48
53
|
...native.options,
|
|
49
|
-
honoRouteBinder
|
|
50
|
-
runtimeAdapters: {
|
|
51
|
-
...runtimeAdapters,
|
|
52
|
-
effect
|
|
53
|
-
}
|
|
54
|
+
honoRouteBinder
|
|
54
55
|
};
|
|
56
|
+
native.includeEntries = [
|
|
57
|
+
...new Set([
|
|
58
|
+
...native.includeEntries ?? [],
|
|
59
|
+
honoRouteBinder
|
|
60
|
+
])
|
|
61
|
+
];
|
|
55
62
|
return {
|
|
56
63
|
plugins
|
|
57
64
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "node:module";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import {
|
|
3
|
-
import { transform } from "@swc/core";
|
|
2
|
+
import { fs, upath } from "@modern-js/utils";
|
|
4
3
|
const RELEASE_IDENTITY_GLOBAL_NAMES = [
|
|
5
4
|
'ULTRAMODERN_BUILD_MARKER',
|
|
6
5
|
'ULTRAMODERN_SOURCE_REVISION',
|
|
@@ -15,37 +14,18 @@ const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>fs.readdi
|
|
|
15
14
|
absolutePath
|
|
16
15
|
] : [];
|
|
17
16
|
})).then((nested)=>nested.flat()));
|
|
18
|
-
const serializeServerGlobalVars = (options)=>{
|
|
19
|
-
const globalVars = applyOptionsChain({}, options, {
|
|
20
|
-
env: 'server',
|
|
21
|
-
target: 'node'
|
|
22
|
-
});
|
|
23
|
-
return Object.fromEntries(Object.entries(globalVars).map(([key, value])=>{
|
|
24
|
-
let serialized;
|
|
25
|
-
try {
|
|
26
|
-
serialized = JSON.stringify(value);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`, {
|
|
29
|
-
cause: error
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
if (void 0 === serialized) throw new Error(`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`);
|
|
33
|
-
return [
|
|
34
|
-
key,
|
|
35
|
-
serialized
|
|
36
|
-
];
|
|
37
|
-
}));
|
|
38
|
-
};
|
|
39
17
|
const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
|
|
40
18
|
if (0 === Object.keys(globalVars).length) return;
|
|
41
19
|
return Promise.all(outputDirectories.map((directory)=>fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
|
|
42
20
|
const filesPromise = Promise.all(existingDirectories.filter((directory)=>void 0 !== directory).map(collectJavaScriptFiles)).then((files)=>files.flat());
|
|
43
|
-
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)
|
|
21
|
+
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`${globalVars[name]};`).join('')}\n` : '';
|
|
44
22
|
return filesPromise.then((files)=>Promise.all(files.map((filename)=>fs.readFile(filename, 'utf8').then((source)=>{
|
|
45
23
|
const sourceMapFilename = `${filename}.map`;
|
|
46
24
|
return fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
|
|
47
25
|
const inputSourceMapPromise = sourceMapExists ? fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
|
|
48
|
-
return inputSourceMapPromise.then((inputSourceMap)=>
|
|
26
|
+
return inputSourceMapPromise.then(async (inputSourceMap)=>{
|
|
27
|
+
const { transform } = await import("@swc/core");
|
|
28
|
+
return transform(`${releaseIdentityBanner}${source}`, {
|
|
49
29
|
filename,
|
|
50
30
|
inputSourceMap,
|
|
51
31
|
sourceMaps: sourceMapExists,
|
|
@@ -65,9 +45,10 @@ const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resol
|
|
|
65
45
|
}
|
|
66
46
|
}).then((result)=>fs.writeFile(filename, result.code).then(()=>{
|
|
67
47
|
if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return fs.writeFile(sourceMapFilename, result.map);
|
|
68
|
-
}))
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
69
50
|
});
|
|
70
51
|
}))));
|
|
71
52
|
}).then(()=>void 0);
|
|
72
53
|
});
|
|
73
|
-
export {
|
|
54
|
+
export { transformServerGlobalVars };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import '@modern-js/server-runtime-extensions/server-config';
|
|
2
2
|
import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
3
|
+
export type { BffRuntimeBuildIdentity, BffRuntimeBuildIdentityProvider, WithBffRuntimeBuildIdentity, } from './runtime-build-identity.js';
|
|
3
4
|
export declare const bffPlugin: () => CliPlugin<AppTools>;
|
|
4
5
|
export default bffPlugin;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BffCompilation } from '@modern-js/app-tools';
|
|
2
|
+
import type { CLIPluginExtends } from '@modern-js/plugin/cli';
|
|
3
|
+
export interface BffRuntimeBuildIdentity {
|
|
4
|
+
readonly buildMarker: string;
|
|
5
|
+
readonly sourceRevision: string;
|
|
6
|
+
}
|
|
7
|
+
export type BffRuntimeBuildIdentityProvider = (compilation: BffCompilation) => Promise<BffRuntimeBuildIdentity>;
|
|
8
|
+
export type WithBffRuntimeBuildIdentity<Extends extends CLIPluginExtends> = Extends & {
|
|
9
|
+
extendContext: Extends['extendContext'] & {
|
|
10
|
+
resolveBffRuntimeBuildIdentity?: BffRuntimeBuildIdentityProvider;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
declare module '@modern-js/app-tools' {
|
|
14
|
+
interface AppToolsExtendContext {
|
|
15
|
+
resolveBffRuntimeBuildIdentity?: BffRuntimeBuildIdentityProvider;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
type ServerGlobalVarsOptions = NonNullable<NonNullable<AppTools['config']>['source']>['globalVars'];
|
|
4
|
-
export declare const serializeServerGlobalVars: (options: ServerGlobalVarsOptions) => SerializedGlobalVars;
|
|
5
|
-
export declare const transformServerGlobalVars: (outputDirectories: string[], globalVars: SerializedGlobalVars) => Promise<undefined>;
|
|
6
|
-
export {};
|
|
1
|
+
import type { SerializedServerGlobalVars } from '@modern-js/plugin-bff-extensions/server-global-vars';
|
|
2
|
+
export declare const transformServerGlobalVars: (outputDirectories: string[], globalVars: SerializedServerGlobalVars) => Promise<undefined>;
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"homepage": "https://github.com/
|
|
2
|
+
"homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
|
|
3
3
|
"bugs": {
|
|
4
|
-
"url": "https://github.com/
|
|
4
|
+
"url": "https://github.com/ultramodernjs/ultramodern.js/issues"
|
|
5
5
|
},
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"version": "3.9.
|
|
7
|
+
"version": "3.9.3-ultramodern.10",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
10
|
"src"
|
|
11
11
|
],
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": ">=26.
|
|
13
|
+
"node": ">=26.12.0"
|
|
14
14
|
},
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"publishConfig": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"description": "Fork-owned BFF build lifecycle, instrumentation, and producer SDK composition.",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/
|
|
23
|
+
"url": "git+https://github.com/ultramodernjs/ultramodern.js.git",
|
|
24
24
|
"directory": "packages/cli/plugin-bff-build-extensions"
|
|
25
25
|
},
|
|
26
26
|
"main": "./dist/cjs/index.js",
|
|
@@ -65,19 +65,19 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.
|
|
69
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.
|
|
70
|
-
"@modern-js/plugin-bff": "npm:@bleedingdev/modern-js-plugin-bff@3.9.
|
|
71
|
-
"@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.
|
|
72
|
-
"@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.
|
|
73
|
-
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.
|
|
74
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.
|
|
75
|
-
"@swc/core": "1.16.
|
|
68
|
+
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.3-ultramodern.10",
|
|
69
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.3-ultramodern.10",
|
|
70
|
+
"@modern-js/plugin-bff": "npm:@bleedingdev/modern-js-plugin-bff@3.9.3-ultramodern.10",
|
|
71
|
+
"@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.3-ultramodern.10",
|
|
72
|
+
"@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.3-ultramodern.10",
|
|
73
|
+
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.3-ultramodern.10",
|
|
74
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.3-ultramodern.10",
|
|
75
|
+
"@swc/core": "1.16.18"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@effect/opentelemetry": "4.0.
|
|
79
|
-
"@modern-js/app-tools": "3.9.
|
|
80
|
-
"effect": "4.0.
|
|
78
|
+
"@effect/opentelemetry": "4.0.5",
|
|
79
|
+
"@modern-js/app-tools": "3.9.3-ultramodern.10",
|
|
80
|
+
"effect": "4.0.5"
|
|
81
81
|
},
|
|
82
82
|
"peerDependenciesMeta": {
|
|
83
83
|
"@effect/opentelemetry": {
|
|
@@ -88,25 +88,28 @@
|
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@effect/opentelemetry": "4.0.
|
|
92
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.
|
|
93
|
-
"@modern-js/bff-effect": "npm:@bleedingdev/modern-js-bff-effect@3.9.
|
|
94
|
-
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.
|
|
95
|
-
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.
|
|
96
|
-
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.
|
|
97
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.
|
|
98
|
-
"@rsbuild/core": "2.2.
|
|
99
|
-
"@rslib/core": "1.0.
|
|
91
|
+
"@effect/opentelemetry": "4.0.5",
|
|
92
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.3-ultramodern.10",
|
|
93
|
+
"@modern-js/bff-effect": "npm:@bleedingdev/modern-js-bff-effect@3.9.3-ultramodern.10",
|
|
94
|
+
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.3-ultramodern.10",
|
|
95
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.3-ultramodern.10",
|
|
96
|
+
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.3-ultramodern.10",
|
|
97
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.3-ultramodern.10",
|
|
98
|
+
"@rsbuild/core": "2.2.13",
|
|
99
|
+
"@rslib/core": "1.0.3",
|
|
100
100
|
"@scripts/rstest-config": "2.66.0",
|
|
101
|
-
"@types/node": "^26.
|
|
102
|
-
"
|
|
103
|
-
"effect": "4.0.0-rc.112",
|
|
101
|
+
"@types/node": "^26.6.5",
|
|
102
|
+
"effect": "4.0.5",
|
|
104
103
|
"esbuild": "^0.28.2",
|
|
105
|
-
"
|
|
104
|
+
"typescript": "7.0.2",
|
|
105
|
+
"zod": "^4.6.7"
|
|
106
106
|
},
|
|
107
107
|
"scripts": {
|
|
108
108
|
"dev": "rslib build --watch",
|
|
109
109
|
"build": "rslib build",
|
|
110
110
|
"test": "rstest"
|
|
111
|
+
},
|
|
112
|
+
"ultramodern": {
|
|
113
|
+
"sourceName": "@modern-js/plugin-bff-build-extensions"
|
|
111
114
|
}
|
|
112
115
|
}
|
package/src/client-artifacts.ts
CHANGED
|
@@ -7,10 +7,9 @@ import { ApiRouter } from '@modern-js/bff-core';
|
|
|
7
7
|
import type { CLIPluginAPI } from '@modern-js/plugin';
|
|
8
8
|
import {
|
|
9
9
|
buildOperationContractMap,
|
|
10
|
-
deriveOperationVersion,
|
|
11
10
|
type OperationContractMap,
|
|
11
|
+
resolveOperationProducer,
|
|
12
12
|
} from '@modern-js/server-runtime-extensions/bff-policy/node';
|
|
13
|
-
import { fs, upath as path } from '@modern-js/utils';
|
|
14
13
|
|
|
15
14
|
export const BFF_REQUEST_RUNTIME =
|
|
16
15
|
'@modern-js/runtime-extensions/request-policy';
|
|
@@ -46,6 +45,7 @@ export function registerBffClientArtifacts(
|
|
|
46
45
|
resourcePath: sourceEffectEntry,
|
|
47
46
|
prefix: generation.prefix,
|
|
48
47
|
requestId: generation.requestId,
|
|
48
|
+
globalVars: config.source.globalVars,
|
|
49
49
|
});
|
|
50
50
|
if (operationContracts === null) {
|
|
51
51
|
throw new Error(
|
|
@@ -66,14 +66,13 @@ export function registerBffClientArtifacts(
|
|
|
66
66
|
httpMethodDecider: generation.httpMethodDecider,
|
|
67
67
|
isBuild: true,
|
|
68
68
|
});
|
|
69
|
-
const packageJson = await fs.readJSON(
|
|
70
|
-
path.join(generation.appDirectory, 'package.json'),
|
|
71
|
-
);
|
|
72
69
|
const operationContracts = generation.existLambda
|
|
73
70
|
? buildOperationContractMap({
|
|
74
71
|
handlers: await router.getApiHandlers(),
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
...resolveOperationProducer({
|
|
73
|
+
directories: [generation.apiDirectory, generation.appDirectory],
|
|
74
|
+
requestId: generation.requestId || 'default',
|
|
75
|
+
}),
|
|
77
76
|
})
|
|
78
77
|
: {};
|
|
79
78
|
metadata.set(generation, {
|
package/src/compile.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { AppTools, BffCompilation } from '@modern-js/app-tools';
|
|
2
2
|
import type { CLIPluginAPI } from '@modern-js/plugin';
|
|
3
3
|
import {
|
|
4
|
+
type SerializedServerGlobalVars,
|
|
4
5
|
serializeServerGlobalVars,
|
|
5
|
-
|
|
6
|
-
} from './
|
|
6
|
+
} from '@modern-js/plugin-bff-extensions/server-global-vars';
|
|
7
|
+
import type { BffRuntimeBuildIdentity } from './runtime-build-identity';
|
|
8
|
+
import { transformServerGlobalVars } from './server-global-vars';
|
|
7
9
|
|
|
8
10
|
export function registerBffCompilation(api: CLIPluginAPI<AppTools>) {
|
|
9
|
-
const serialized = new WeakMap<
|
|
10
|
-
BffCompilation,
|
|
11
|
-
ReturnType<typeof serializeServerGlobalVars>
|
|
12
|
-
>();
|
|
11
|
+
const serialized = new WeakMap<BffCompilation, SerializedServerGlobalVars>();
|
|
13
12
|
api.onBeforeBffCompile(context => {
|
|
14
13
|
serialized.set(
|
|
15
14
|
context,
|
|
@@ -21,6 +20,37 @@ export function registerBffCompilation(api: CLIPluginAPI<AppTools>) {
|
|
|
21
20
|
if (!globals)
|
|
22
21
|
throw new Error('BFF compilation completed without its before hook.');
|
|
23
22
|
serialized.delete(context);
|
|
23
|
+
const appContext = api.getAppContext();
|
|
24
|
+
const resolveIdentity = appContext.resolveBffRuntimeBuildIdentity;
|
|
25
|
+
if (appContext.apiOnly !== true && resolveIdentity !== undefined) {
|
|
26
|
+
if (appContext.appDirectory !== context.appDirectory) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'BFF runtime build identity provider does not belong to this application.',
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (typeof resolveIdentity !== 'function') {
|
|
32
|
+
throw new Error('BFF runtime build identity provider is invalid.');
|
|
33
|
+
}
|
|
34
|
+
const identity: BffRuntimeBuildIdentity = await resolveIdentity(context);
|
|
35
|
+
if (
|
|
36
|
+
identity === null ||
|
|
37
|
+
typeof identity !== 'object' ||
|
|
38
|
+
!Object.isFrozen(identity)
|
|
39
|
+
) {
|
|
40
|
+
throw new Error('BFF runtime build identity must be immutable.');
|
|
41
|
+
}
|
|
42
|
+
const { buildMarker, sourceRevision } = identity;
|
|
43
|
+
if (
|
|
44
|
+
typeof buildMarker !== 'string' ||
|
|
45
|
+
!/^[0-9a-f]{64}$/u.test(buildMarker) ||
|
|
46
|
+
typeof sourceRevision !== 'string' ||
|
|
47
|
+
sourceRevision.trim().length === 0
|
|
48
|
+
) {
|
|
49
|
+
throw new Error('BFF runtime build identity is not finalized.');
|
|
50
|
+
}
|
|
51
|
+
globals.ULTRAMODERN_BUILD_MARKER = JSON.stringify(buildMarker);
|
|
52
|
+
globals.ULTRAMODERN_SOURCE_REVISION = JSON.stringify(sourceRevision);
|
|
53
|
+
}
|
|
24
54
|
await transformServerGlobalVars([...context.outputDirectories], globals);
|
|
25
55
|
if (api.getAppContext().bffRuntimeFramework !== 'effect') return;
|
|
26
56
|
// Hosted APIs use the producer's compiled entry; it is not emitted by this app.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import type { ClientCodegenPlugin } from '@modern-js/bff-core';
|
|
3
2
|
import {
|
|
4
3
|
buildOperationContractMap,
|
|
5
4
|
createOperationSchemaHash,
|
|
6
|
-
|
|
5
|
+
resolveOperationProducer,
|
|
7
6
|
} from '@modern-js/server-runtime-extensions/bff-policy/node';
|
|
8
7
|
|
|
9
8
|
const REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
@@ -11,14 +10,11 @@ const REQUEST_RUNTIME = '@modern-js/runtime-extensions/request-policy';
|
|
|
11
10
|
export const modifyClient: ClientCodegenPlugin = (draft, context) => {
|
|
12
11
|
const { appDir, requestId } = context.options;
|
|
13
12
|
draft.requestCreator = context.options.requestCreator || REQUEST_RUNTIME;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
const operationVersion = deriveOperationVersion(version);
|
|
21
|
-
const normalizedRequestId = requestId || 'default';
|
|
13
|
+
const { operationVersion, requestId: normalizedRequestId } =
|
|
14
|
+
resolveOperationProducer({
|
|
15
|
+
directories: [context.options.apiDir, appDir],
|
|
16
|
+
requestId: requestId || 'default',
|
|
17
|
+
});
|
|
22
18
|
const operationContracts = buildOperationContractMap({
|
|
23
19
|
handlers: [...context.handlerInfos],
|
|
24
20
|
requestId: normalizedRequestId,
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,12 @@ import { registerBffCompilation } from './compile';
|
|
|
11
11
|
import { registerBffGeneratedEntries } from './generated-entries';
|
|
12
12
|
import { resolveSelfModule } from './self-module';
|
|
13
13
|
|
|
14
|
+
export type {
|
|
15
|
+
BffRuntimeBuildIdentity,
|
|
16
|
+
BffRuntimeBuildIdentityProvider,
|
|
17
|
+
WithBffRuntimeBuildIdentity,
|
|
18
|
+
} from './runtime-build-identity';
|
|
19
|
+
|
|
14
20
|
export const bffPlugin = (): CliPlugin<AppTools> => ({
|
|
15
21
|
name: '@modern-js/plugin-bff-build-extensions',
|
|
16
22
|
usePlugins: [nativeBffPlugin()],
|
|
@@ -50,8 +56,6 @@ export const bffPlugin = (): CliPlugin<AppTools> => ({
|
|
|
50
56
|
throw new Error('Expected exactly one native BFF server plugin.');
|
|
51
57
|
}
|
|
52
58
|
const native = nativePlugins[0]!;
|
|
53
|
-
const runtimeAdapters = native.options?.runtimeAdapters ?? {};
|
|
54
|
-
const effect = '@modern-js/plugin-bff-extensions/effect-adapter';
|
|
55
59
|
const honoRouteBinder = '@modern-js/plugin-bff-extensions/hono/node';
|
|
56
60
|
if (
|
|
57
61
|
native.options?.honoRouteBinder !== undefined &&
|
|
@@ -59,19 +63,19 @@ export const bffPlugin = (): CliPlugin<AppTools> => ({
|
|
|
59
63
|
) {
|
|
60
64
|
throw new Error('The Hono BFF route binder is already configured.');
|
|
61
65
|
}
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'The Effect BFF runtime adapter is already configured.',
|
|
68
|
-
);
|
|
66
|
+
if (runtimeFramework !== 'hono') {
|
|
67
|
+
plugins.splice(plugins.indexOf(native), 1, {
|
|
68
|
+
name: '@modern-js/plugin-bff-extensions/effect-server',
|
|
69
|
+
});
|
|
70
|
+
return { plugins };
|
|
69
71
|
}
|
|
70
72
|
native.options = {
|
|
71
73
|
...native.options,
|
|
72
74
|
honoRouteBinder,
|
|
73
|
-
runtimeAdapters: { ...runtimeAdapters, effect },
|
|
74
75
|
};
|
|
76
|
+
native.includeEntries = [
|
|
77
|
+
...new Set([...(native.includeEntries ?? []), honoRouteBinder]),
|
|
78
|
+
];
|
|
75
79
|
return { plugins };
|
|
76
80
|
});
|
|
77
81
|
const metadata = new WeakMap<BffGeneration, BffGenerationMetadata>();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BffCompilation } from '@modern-js/app-tools';
|
|
2
|
+
import type { CLIPluginExtends } from '@modern-js/plugin/cli';
|
|
3
|
+
|
|
4
|
+
export interface BffRuntimeBuildIdentity {
|
|
5
|
+
readonly buildMarker: string;
|
|
6
|
+
readonly sourceRevision: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type BffRuntimeBuildIdentityProvider = (
|
|
10
|
+
compilation: BffCompilation,
|
|
11
|
+
) => Promise<BffRuntimeBuildIdentity>;
|
|
12
|
+
|
|
13
|
+
export type WithBffRuntimeBuildIdentity<Extends extends CLIPluginExtends> =
|
|
14
|
+
Extends & {
|
|
15
|
+
extendContext: Extends['extendContext'] & {
|
|
16
|
+
resolveBffRuntimeBuildIdentity?: BffRuntimeBuildIdentityProvider;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
declare module '@modern-js/app-tools' {
|
|
21
|
+
interface AppToolsExtendContext {
|
|
22
|
+
resolveBffRuntimeBuildIdentity?: BffRuntimeBuildIdentityProvider;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import { applyOptionsChain, fs, upath as path } from '@modern-js/utils';
|
|
4
|
-
import { transform } from '@swc/core';
|
|
5
|
-
|
|
6
|
-
type SerializedGlobalVars = Record<string, string>;
|
|
7
|
-
type ServerGlobalVarsOptions = NonNullable<
|
|
8
|
-
NonNullable<AppTools['config']>['source']
|
|
9
|
-
>['globalVars'];
|
|
1
|
+
import type { SerializedServerGlobalVars } from '@modern-js/plugin-bff-extensions/server-global-vars';
|
|
2
|
+
import { fs, upath as path } from '@modern-js/utils';
|
|
10
3
|
|
|
11
4
|
const RELEASE_IDENTITY_GLOBAL_NAMES = [
|
|
12
5
|
'ULTRAMODERN_BUILD_MARKER',
|
|
@@ -29,41 +22,9 @@ const collectJavaScriptFiles = (directory: string): Promise<string[]> =>
|
|
|
29
22
|
).then(nested => nested.flat()),
|
|
30
23
|
);
|
|
31
24
|
|
|
32
|
-
export const serializeServerGlobalVars = (
|
|
33
|
-
options: ServerGlobalVarsOptions,
|
|
34
|
-
): SerializedGlobalVars => {
|
|
35
|
-
const globalVars = applyOptionsChain<
|
|
36
|
-
Record<string, unknown>,
|
|
37
|
-
{ env: string; target: RsbuildTarget }
|
|
38
|
-
>({}, options, {
|
|
39
|
-
env: 'server',
|
|
40
|
-
target: 'node',
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return Object.fromEntries(
|
|
44
|
-
Object.entries(globalVars).map(([key, value]) => {
|
|
45
|
-
let serialized: string | undefined;
|
|
46
|
-
try {
|
|
47
|
-
serialized = JSON.stringify(value);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`,
|
|
51
|
-
{ cause: error },
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (serialized === undefined) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`source.globalVars["${key}"] cannot be serialized exactly for BFF compilation.`,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return [key, serialized];
|
|
60
|
-
}),
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
25
|
export const transformServerGlobalVars = (
|
|
65
26
|
outputDirectories: string[],
|
|
66
|
-
globalVars:
|
|
27
|
+
globalVars: SerializedServerGlobalVars,
|
|
67
28
|
) =>
|
|
68
29
|
Promise.resolve().then(() => {
|
|
69
30
|
if (Object.keys(globalVars).length === 0) {
|
|
@@ -87,7 +48,8 @@ export const transformServerGlobalVars = (
|
|
|
87
48
|
const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every(
|
|
88
49
|
name => Object.hasOwn(globalVars, name),
|
|
89
50
|
)
|
|
90
|
-
?
|
|
51
|
+
? // String statements, so a following "use strict" stays in the prologue.
|
|
52
|
+
`${RELEASE_IDENTITY_GLOBAL_NAMES.map(name => `${globalVars[name]};`).join('')}\n`
|
|
91
53
|
: '';
|
|
92
54
|
|
|
93
55
|
return filesPromise.then(files =>
|
|
@@ -103,8 +65,9 @@ export const transformServerGlobalVars = (
|
|
|
103
65
|
? fs.readFile(sourceMapFilename, 'utf8')
|
|
104
66
|
: Promise.resolve(undefined);
|
|
105
67
|
|
|
106
|
-
return inputSourceMapPromise.then(inputSourceMap =>
|
|
107
|
-
transform
|
|
68
|
+
return inputSourceMapPromise.then(async inputSourceMap => {
|
|
69
|
+
const { transform } = await import('@swc/core');
|
|
70
|
+
return transform(`${releaseIdentityBanner}${source}`, {
|
|
108
71
|
filename,
|
|
109
72
|
inputSourceMap,
|
|
110
73
|
sourceMaps: sourceMapExists,
|
|
@@ -133,8 +96,8 @@ export const transformServerGlobalVars = (
|
|
|
133
96
|
}
|
|
134
97
|
return undefined;
|
|
135
98
|
}),
|
|
136
|
-
)
|
|
137
|
-
);
|
|
99
|
+
);
|
|
100
|
+
});
|
|
138
101
|
});
|
|
139
102
|
}),
|
|
140
103
|
),
|