@bleedingdev/modern-js-plugin-bff 3.2.0-ultramodern.120 → 3.2.0-ultramodern.122
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/runtime/data-platform/index.js +2 -13
- package/dist/cjs/runtime/effect/adapter.js +78 -9
- package/dist/cjs/runtime/effect/edge.js +13 -17
- package/dist/cjs/runtime/effect/endpoint-contracts.js +130 -0
- package/dist/cjs/runtime/effect/handler.js +50 -5
- package/dist/cjs/runtime/effect/module.js +16 -7
- package/dist/cjs/runtime/effect/operation-context.js +1 -13
- package/dist/cjs/runtime/effect-client/runtime.js +266 -0
- package/dist/cjs/runtime/hono/adapter.js +21 -9
- package/dist/cjs/runtime/safe-failure.js +83 -0
- package/dist/cjs/utils/clientGenerator.js +4 -4
- package/dist/cjs/utils/crossProjectServerPolicy.js +104 -0
- package/dist/cjs/utils/effectClientGenerator.js +90 -483
- package/dist/esm/runtime/data-platform/index.mjs +2 -13
- package/dist/esm/runtime/effect/adapter.mjs +78 -9
- package/dist/esm/runtime/effect/edge.mjs +2 -9
- package/dist/esm/runtime/effect/endpoint-contracts.mjs +68 -0
- package/dist/esm/runtime/effect/handler.mjs +36 -4
- package/dist/esm/runtime/effect/module.mjs +17 -8
- package/dist/esm/runtime/effect/operation-context.mjs +1 -13
- package/dist/esm/runtime/effect-client/runtime.mjs +228 -0
- package/dist/esm/runtime/hono/adapter.mjs +21 -9
- package/dist/esm/runtime/safe-failure.mjs +45 -0
- package/dist/esm/utils/clientGenerator.mjs +5 -5
- package/dist/esm/utils/crossProjectServerPolicy.mjs +50 -0
- package/dist/esm/utils/effectClientGenerator.mjs +88 -484
- package/dist/esm-node/runtime/data-platform/index.mjs +2 -13
- package/dist/esm-node/runtime/effect/adapter.mjs +78 -9
- package/dist/esm-node/runtime/effect/edge.mjs +2 -9
- package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +69 -0
- package/dist/esm-node/runtime/effect/handler.mjs +36 -4
- package/dist/esm-node/runtime/effect/module.mjs +17 -8
- package/dist/esm-node/runtime/effect/operation-context.mjs +1 -13
- package/dist/esm-node/runtime/effect-client/runtime.mjs +229 -0
- package/dist/esm-node/runtime/hono/adapter.mjs +21 -9
- package/dist/esm-node/runtime/safe-failure.mjs +46 -0
- package/dist/esm-node/utils/clientGenerator.mjs +5 -5
- package/dist/esm-node/utils/crossProjectServerPolicy.mjs +52 -0
- package/dist/esm-node/utils/effectClientGenerator.mjs +88 -484
- package/dist/types/runtime/effect/adapter.d.ts +25 -0
- package/dist/types/runtime/effect/endpoint-contracts.d.ts +62 -0
- package/dist/types/runtime/effect/handler.d.ts +30 -0
- package/dist/types/runtime/effect/module.d.ts +21 -1
- package/dist/types/runtime/effect-client/runtime.d.ts +71 -0
- package/dist/types/runtime/hono/adapter.d.ts +3 -0
- package/dist/types/runtime/safe-failure.d.ts +1 -0
- package/dist/types/utils/crossProjectServerPolicy.d.ts +35 -0
- package/dist/types/utils/effectClientGenerator.d.ts +15 -1
- package/package.json +24 -12
|
@@ -53,6 +53,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
53
53
|
validateRequestEnvelope: ()=>validateRequestEnvelope,
|
|
54
54
|
validateSelectionPlan: ()=>validateSelectionPlan
|
|
55
55
|
});
|
|
56
|
+
const create_request_namespaceObject = require("@modern-js/create-request");
|
|
56
57
|
const api_namespaceObject = require("@opentelemetry/api");
|
|
57
58
|
const DATA_BATCH_TRANSPORT_OTEL_EVENT = 'modernjs.data.batch';
|
|
58
59
|
function createDataBatchTransportTelemetryAttributes(event) {
|
|
@@ -78,7 +79,6 @@ function emitDataBatchTransportEvent(onEvent, event) {
|
|
|
78
79
|
const DEFAULT_DATA_ENVELOPE_HEADER = 'x-modernjs-data-envelope';
|
|
79
80
|
const DEFAULT_DATA_BATCH_ENDPOINT = '/_data/batch';
|
|
80
81
|
const DEFAULT_DATA_BATCH_HEADER = 'x-modernjs-data-batch';
|
|
81
|
-
const TRACEPARENT_REGEX = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i;
|
|
82
82
|
function isPlainObject(value) {
|
|
83
83
|
if ('object' != typeof value || null === value || Array.isArray(value)) return false;
|
|
84
84
|
const proto = Object.getPrototypeOf(value);
|
|
@@ -175,18 +175,7 @@ function isValidHex(value, length) {
|
|
|
175
175
|
return value.length === length && /^[0-9a-f]+$/.test(value);
|
|
176
176
|
}
|
|
177
177
|
function parseTraceparentHeader(header) {
|
|
178
|
-
|
|
179
|
-
if (!match) return null;
|
|
180
|
-
const traceId = match[1].toLowerCase();
|
|
181
|
-
const spanId = match[2].toLowerCase();
|
|
182
|
-
const flags = match[3].toLowerCase();
|
|
183
|
-
if (isAllZeroHex(traceId) || isAllZeroHex(spanId)) return null;
|
|
184
|
-
const sampled = (0x1 & Number.parseInt(flags, 16)) === 1;
|
|
185
|
-
return {
|
|
186
|
-
traceId,
|
|
187
|
-
spanId,
|
|
188
|
-
sampled
|
|
189
|
-
};
|
|
178
|
+
return (0, create_request_namespaceObject.parseTraceparent)(header) ?? null;
|
|
190
179
|
}
|
|
191
180
|
function formatTraceparentHeader(trace) {
|
|
192
181
|
const traceId = trace.traceId.toLowerCase();
|
|
@@ -39,10 +39,15 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39
39
|
__webpack_require__.d(__webpack_exports__, {
|
|
40
40
|
EffectAdapter: ()=>EffectAdapter
|
|
41
41
|
});
|
|
42
|
+
const bff_core_namespaceObject = require("@modern-js/bff-core");
|
|
42
43
|
const utils_namespaceObject = require("@modern-js/utils");
|
|
44
|
+
const httpapi_namespaceObject = require("effect/unstable/httpapi");
|
|
43
45
|
const external_path_namespaceObject = require("path");
|
|
44
46
|
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
47
|
+
const crossProjectServerPolicy_js_namespaceObject = require("../../utils/crossProjectServerPolicy.js");
|
|
48
|
+
const external_safe_failure_js_namespaceObject = require("../safe-failure.js");
|
|
45
49
|
const external_context_js_namespaceObject = require("./context.js");
|
|
50
|
+
const external_endpoint_contracts_js_namespaceObject = require("./endpoint-contracts.js");
|
|
46
51
|
const external_module_js_namespaceObject = require("./module.js");
|
|
47
52
|
const before = [
|
|
48
53
|
'custom-server-hook',
|
|
@@ -88,10 +93,73 @@ class EffectAdapter {
|
|
|
88
93
|
const entryWithoutExt = configuredEntry ? external_path_default().isAbsolute(configuredEntry) ? configuredEntry : external_path_default().resolve(appDirectory || process.cwd(), configuredEntry) : defaultEntry;
|
|
89
94
|
return (0, utils_namespaceObject.findExists)(JS_OR_TS_EXTS.map((ext)=>`${entryWithoutExt}${ext}`));
|
|
90
95
|
}
|
|
96
|
+
isApiRequestPath(requestPath, prefix, enableHandleWeb) {
|
|
97
|
+
if (!enableHandleWeb) return true;
|
|
98
|
+
const normalized = normalizePrefix(prefix);
|
|
99
|
+
if (!normalized) return true;
|
|
100
|
+
return requestPath === normalized || requestPath.startsWith(`${normalized}/`);
|
|
101
|
+
}
|
|
102
|
+
async collectLambdaContractSources() {
|
|
103
|
+
try {
|
|
104
|
+
const serverContext = this.api.getServerContext();
|
|
105
|
+
const appDir = serverContext.distDirectory || serverContext.appDirectory;
|
|
106
|
+
if (!appDir) return [];
|
|
107
|
+
const apiDir = 'string' == typeof serverContext.apiDirectory ? serverContext.apiDirectory : external_path_default().resolve(appDir, utils_namespaceObject.API_DIR);
|
|
108
|
+
const lambdaDir = 'string' == typeof serverContext.lambdaDirectory ? serverContext.lambdaDirectory : external_path_default().join(apiDir, 'lambda');
|
|
109
|
+
if (!await utils_namespaceObject.fs.pathExists(lambdaDir)) return [];
|
|
110
|
+
const apiRouter = new bff_core_namespaceObject.ApiRouter({
|
|
111
|
+
appDir,
|
|
112
|
+
apiDir,
|
|
113
|
+
lambdaDir,
|
|
114
|
+
prefix: this.prefix,
|
|
115
|
+
httpMethodDecider: this.api.getServerConfig()?.bff?.httpMethodDecider
|
|
116
|
+
});
|
|
117
|
+
const handlerInfos = await apiRouter.getApiHandlers();
|
|
118
|
+
return handlerInfos.map((info)=>({
|
|
119
|
+
name: info.name,
|
|
120
|
+
httpMethod: info.httpMethod,
|
|
121
|
+
routePath: info.routePath,
|
|
122
|
+
filename: info.filename,
|
|
123
|
+
handler: info.handler
|
|
124
|
+
}));
|
|
125
|
+
} catch (error) {
|
|
126
|
+
utils_namespaceObject.logger.warn(`[BFF][Effect] Failed to derive lambda operation contracts for the cross-project policy: ${String(error)}`);
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async refreshCrossProjectPolicy(mod) {
|
|
131
|
+
let contractSources = [];
|
|
132
|
+
if (mod) try {
|
|
133
|
+
const api = await (0, external_endpoint_contracts_js_namespaceObject.extractHttpApiFromModule)(mod, httpapi_namespaceObject.HttpApi.isHttpApi);
|
|
134
|
+
if (api) {
|
|
135
|
+
const reflect = (apiValue, handlers)=>httpapi_namespaceObject.HttpApi.reflect(apiValue, {
|
|
136
|
+
onGroup: handlers.onGroup ?? (()=>{}),
|
|
137
|
+
onEndpoint: handlers.onEndpoint
|
|
138
|
+
});
|
|
139
|
+
contractSources = (0, external_endpoint_contracts_js_namespaceObject.toOperationContractSources)((0, external_endpoint_contracts_js_namespaceObject.collectEffectEndpoints)(reflect, api, this.prefix));
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
utils_namespaceObject.logger.warn(`[BFF][Effect] Failed to reflect HttpApi endpoints for the cross-project policy: ${String(error)}`);
|
|
143
|
+
}
|
|
144
|
+
let policy = (0, crossProjectServerPolicy_js_namespaceObject.resolveAdapterCrossProjectPolicy)(this.api, contractSources);
|
|
145
|
+
if (policy?.enabled) {
|
|
146
|
+
const lambdaSources = await this.collectLambdaContractSources();
|
|
147
|
+
if (lambdaSources.length > 0) {
|
|
148
|
+
contractSources = [
|
|
149
|
+
...contractSources,
|
|
150
|
+
...lambdaSources
|
|
151
|
+
];
|
|
152
|
+
policy = (0, crossProjectServerPolicy_js_namespaceObject.resolveAdapterCrossProjectPolicy)(this.api, contractSources);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
this.crossProjectPolicy = policy;
|
|
156
|
+
if (this.crossProjectPolicy?.enabled && 0 === contractSources.length) utils_namespaceObject.logger.warn('[BFF][Effect] Cross-project policy is enabled but no HttpApi endpoints could be reflected; operation-contract matching is disabled for this server (envelope and operation-context checks still apply).');
|
|
157
|
+
}
|
|
91
158
|
async loadEffectHandlerFromModule(mod) {
|
|
92
159
|
return (0, external_module_js_namespaceObject.resolveEffectBffModuleHandler)(mod, {
|
|
93
160
|
openapi: this.api.getServerConfig()?.bff?.effect?.openapi,
|
|
94
161
|
dataPlatform: this.api.getServerConfig()?.bff?.effect?.dataPlatform,
|
|
162
|
+
validateRequest: (request)=>(0, crossProjectServerPolicy_js_namespaceObject.checkCrossProjectPolicyForRequest)(request, this.crossProjectPolicy),
|
|
95
163
|
onWarning: (message)=>{
|
|
96
164
|
utils_namespaceObject.logger.warn(message);
|
|
97
165
|
}
|
|
@@ -121,6 +189,7 @@ class EffectAdapter {
|
|
|
121
189
|
this.handler = null;
|
|
122
190
|
return;
|
|
123
191
|
}
|
|
192
|
+
await this.refreshCrossProjectPolicy(mod);
|
|
124
193
|
const loaded = await this.loadEffectHandlerFromModule(mod);
|
|
125
194
|
if (!loaded) {
|
|
126
195
|
utils_namespaceObject.logger.warn(`[BFF][Effect] Invalid Effect entry module: ${entryFile}. Export { api, layer } or handler.`);
|
|
@@ -129,6 +198,7 @@ class EffectAdapter {
|
|
|
129
198
|
}
|
|
130
199
|
this.handler = loaded.handler;
|
|
131
200
|
this.dispose = loaded.dispose || null;
|
|
201
|
+
this.policyEnforcedInMiddleware = !loaded.appliesRequestValidator;
|
|
132
202
|
}
|
|
133
203
|
async disposeCurrentHandler() {
|
|
134
204
|
if (!this.dispose) return;
|
|
@@ -152,15 +222,7 @@ class EffectAdapter {
|
|
|
152
222
|
} catch (configError) {
|
|
153
223
|
utils_namespaceObject.logger.error(`Error in serverConfig.onError handler: ${configError}`);
|
|
154
224
|
}
|
|
155
|
-
|
|
156
|
-
return new Response(JSON.stringify({
|
|
157
|
-
message: error instanceof Error ? error.message : '[BFF] Internal Server Error'
|
|
158
|
-
}), {
|
|
159
|
-
status,
|
|
160
|
-
headers: {
|
|
161
|
-
'content-type': 'application/json; charset=utf-8'
|
|
162
|
-
}
|
|
163
|
-
});
|
|
225
|
+
return (0, external_safe_failure_js_namespaceObject.createSafeFailureResponse)(error);
|
|
164
226
|
}
|
|
165
227
|
ensureJsonContext(c) {
|
|
166
228
|
const maybeJsonContext = c;
|
|
@@ -187,6 +249,8 @@ class EffectAdapter {
|
|
|
187
249
|
this.effectMiddleware = null;
|
|
188
250
|
this.handler = null;
|
|
189
251
|
this.dispose = null;
|
|
252
|
+
this.prefix = '/api';
|
|
253
|
+
this.policyEnforcedInMiddleware = false;
|
|
190
254
|
this.registerMiddleware = async (options)=>{
|
|
191
255
|
const { prefix, enableHandleWeb } = options;
|
|
192
256
|
const { bffRuntimeFramework, middlewares: globalMiddlewares } = this.api.getServerContext();
|
|
@@ -194,6 +258,7 @@ class EffectAdapter {
|
|
|
194
258
|
this.isEffect = false;
|
|
195
259
|
return;
|
|
196
260
|
}
|
|
261
|
+
this.prefix = prefix || this.prefix;
|
|
197
262
|
await this.reloadHandler();
|
|
198
263
|
this.effectMiddleware = {
|
|
199
264
|
name: 'effect-bff-handler',
|
|
@@ -206,6 +271,10 @@ class EffectAdapter {
|
|
|
206
271
|
if (enableHandleWeb) return void await next();
|
|
207
272
|
return this.handleRuntimeError(new Error('[BFF][Effect] Missing Effect entry. Define api/effect/index or configure bff.effect.entry.'), c);
|
|
208
273
|
}
|
|
274
|
+
if (this.crossProjectPolicy?.enabled && this.policyEnforcedInMiddleware && this.isApiRequestPath(c.req.path, prefix, enableHandleWeb)) {
|
|
275
|
+
const denial = (0, crossProjectServerPolicy_js_namespaceObject.checkCrossProjectPolicyForRequest)(c.req.raw, this.crossProjectPolicy);
|
|
276
|
+
if (denial) return denial;
|
|
277
|
+
}
|
|
209
278
|
let response;
|
|
210
279
|
try {
|
|
211
280
|
const effectRequest = createRequestForMountedPrefix(c.req.raw, prefix);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_modules__ = {
|
|
3
|
+
"../safe-failure" (module) {
|
|
4
|
+
module.exports = require("../safe-failure.js");
|
|
5
|
+
},
|
|
3
6
|
"./handler" (module) {
|
|
4
7
|
module.exports = require("./handler.js");
|
|
5
8
|
},
|
|
@@ -57,16 +60,17 @@ function __webpack_require__(moduleId) {
|
|
|
57
60
|
var __webpack_exports__ = {};
|
|
58
61
|
(()=>{
|
|
59
62
|
__webpack_require__.r(__webpack_exports__);
|
|
60
|
-
var
|
|
61
|
-
var
|
|
62
|
-
var
|
|
63
|
+
var _safe_failure__rspack_import_0 = __webpack_require__("../safe-failure");
|
|
64
|
+
var _module__rspack_import_1 = __webpack_require__("./module");
|
|
65
|
+
var _operation_context__rspack_import_2 = __webpack_require__("./operation-context");
|
|
66
|
+
var _handler__rspack_import_3 = __webpack_require__("./handler");
|
|
63
67
|
var __rspack_reexport = {};
|
|
64
|
-
for(const __rspack_import_key in
|
|
68
|
+
for(const __rspack_import_key in _handler__rspack_import_3)if ([
|
|
65
69
|
"default",
|
|
66
70
|
"dispatchEffectBffRequest",
|
|
67
71
|
"createEffectOperationContext",
|
|
68
72
|
"createEffectBffEdgeHandler"
|
|
69
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
73
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_handler__rspack_import_3[__rspack_import_key];
|
|
70
74
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
71
75
|
function normalizePrefix(prefix) {
|
|
72
76
|
if (!prefix || '/' === prefix) return '';
|
|
@@ -97,7 +101,7 @@ var __webpack_exports__ = {};
|
|
|
97
101
|
env: options.env || {},
|
|
98
102
|
path: originalPath,
|
|
99
103
|
method,
|
|
100
|
-
operationContext: (0,
|
|
104
|
+
operationContext: (0, _operation_context__rspack_import_2.createEffectOperationContext)({
|
|
101
105
|
request: effectRequest,
|
|
102
106
|
env: options.env || {},
|
|
103
107
|
path: originalPath,
|
|
@@ -106,15 +110,7 @@ var __webpack_exports__ = {};
|
|
|
106
110
|
};
|
|
107
111
|
}
|
|
108
112
|
function createRuntimeErrorResponse(error) {
|
|
109
|
-
|
|
110
|
-
return new Response(JSON.stringify({
|
|
111
|
-
message: error instanceof Error ? error.message : '[BFF] Internal Server Error'
|
|
112
|
-
}), {
|
|
113
|
-
status,
|
|
114
|
-
headers: {
|
|
115
|
-
'content-type': 'application/json; charset=utf-8'
|
|
116
|
-
}
|
|
117
|
-
});
|
|
113
|
+
return (0, _safe_failure__rspack_import_0.createSafeFailureResponse)(error);
|
|
118
114
|
}
|
|
119
115
|
async function dispatchEffectBffRequest(handler, request, options = {}) {
|
|
120
116
|
const requestPathname = new URL(request.url).pathname;
|
|
@@ -137,7 +133,7 @@ var __webpack_exports__ = {};
|
|
|
137
133
|
}
|
|
138
134
|
}
|
|
139
135
|
async function createEffectBffEdgeHandler(options) {
|
|
140
|
-
const loaded = await (0,
|
|
136
|
+
const loaded = await (0, _module__rspack_import_1.resolveEffectBffModuleHandler)(options.module, {
|
|
141
137
|
openapi: options.openapi,
|
|
142
138
|
dataPlatform: options.dataPlatform,
|
|
143
139
|
onWarning: options.onWarning
|
|
@@ -156,7 +152,7 @@ var __webpack_exports__ = {};
|
|
|
156
152
|
}
|
|
157
153
|
__webpack_require__.d(__webpack_exports__, {
|
|
158
154
|
createEffectBffEdgeHandler: ()=>createEffectBffEdgeHandler,
|
|
159
|
-
createEffectOperationContext: ()=>
|
|
155
|
+
createEffectOperationContext: ()=>_operation_context__rspack_import_2.createEffectOperationContext,
|
|
160
156
|
dispatchEffectBffRequest: ()=>dispatchEffectBffRequest
|
|
161
157
|
});
|
|
162
158
|
})();
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
collectEffectEndpoints: ()=>collectEffectEndpoints,
|
|
32
|
+
createEffectEndpointContractHash: ()=>createEffectEndpointContractHash,
|
|
33
|
+
createEffectOperationContractSource: ()=>createEffectOperationContractSource,
|
|
34
|
+
ensureLeadingSlash: ()=>ensureLeadingSlash,
|
|
35
|
+
extractHttpApiFromModule: ()=>extractHttpApiFromModule,
|
|
36
|
+
getEffectRoutePath: ()=>getEffectRoutePath,
|
|
37
|
+
normalizeEffectPrefix: ()=>normalizeEffectPrefix,
|
|
38
|
+
resolveEffectApiId: ()=>resolveEffectApiId,
|
|
39
|
+
toOperationContractSources: ()=>toOperationContractSources
|
|
40
|
+
});
|
|
41
|
+
const bff_core_namespaceObject = require("@modern-js/bff-core");
|
|
42
|
+
function ensureLeadingSlash(pathname) {
|
|
43
|
+
return pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
44
|
+
}
|
|
45
|
+
function normalizeEffectPrefix(prefix) {
|
|
46
|
+
if ('/' === prefix) return '';
|
|
47
|
+
return ensureLeadingSlash(prefix || '/api');
|
|
48
|
+
}
|
|
49
|
+
function getEffectRoutePath(prefix, endpointPath) {
|
|
50
|
+
const normalizedPrefix = normalizeEffectPrefix(prefix);
|
|
51
|
+
const normalizedEndpointPath = ensureLeadingSlash(endpointPath);
|
|
52
|
+
const finalEndpointPath = '/' === normalizedEndpointPath ? '' : endpointPath;
|
|
53
|
+
if (!normalizedPrefix && !finalEndpointPath) return '/';
|
|
54
|
+
return `${normalizedPrefix}${finalEndpointPath || ''}`;
|
|
55
|
+
}
|
|
56
|
+
function resolveEffectApiId(api) {
|
|
57
|
+
const fallback = 'EffectHttpApi';
|
|
58
|
+
if ('identifier' in api && 'string' == typeof api.identifier && api.identifier) return api.identifier;
|
|
59
|
+
return fallback;
|
|
60
|
+
}
|
|
61
|
+
function collectEffectEndpoints(reflect, api, prefix) {
|
|
62
|
+
const endpoints = [];
|
|
63
|
+
const apiId = resolveEffectApiId(api);
|
|
64
|
+
reflect(api, {
|
|
65
|
+
onGroup: ()=>{},
|
|
66
|
+
onEndpoint: ({ group, endpoint })=>{
|
|
67
|
+
endpoints.push({
|
|
68
|
+
apiId,
|
|
69
|
+
groupName: String(group.identifier),
|
|
70
|
+
endpointName: String(endpoint.name),
|
|
71
|
+
method: String(endpoint.method).toUpperCase(),
|
|
72
|
+
routePath: getEffectRoutePath(prefix, String(endpoint.path))
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return endpoints.sort((a, b)=>{
|
|
77
|
+
if (a.groupName === b.groupName) return a.endpointName.localeCompare(b.endpointName);
|
|
78
|
+
return a.groupName.localeCompare(b.groupName);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function toOperationContractSources(endpoints) {
|
|
82
|
+
return endpoints.map(createEffectOperationContractSource);
|
|
83
|
+
}
|
|
84
|
+
function createEffectOperationContractSource(endpoint) {
|
|
85
|
+
return {
|
|
86
|
+
name: endpoint.endpointName,
|
|
87
|
+
httpMethod: endpoint.method,
|
|
88
|
+
routePath: endpoint.routePath
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function isRecord(value) {
|
|
92
|
+
return 'object' == typeof value && null !== value;
|
|
93
|
+
}
|
|
94
|
+
async function extractHttpApiFromModule(mod, isHttpApi) {
|
|
95
|
+
if (!isRecord(mod)) return null;
|
|
96
|
+
if (isHttpApi(mod.api)) return mod.api;
|
|
97
|
+
const entry = mod.default;
|
|
98
|
+
if (isRecord(entry) && isHttpApi(entry.api)) return entry.api;
|
|
99
|
+
if ('function' == typeof entry && 0 === entry.length) {
|
|
100
|
+
const output = await entry();
|
|
101
|
+
if (isRecord(output) && isHttpApi(output.api)) return output.api;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function createEffectEndpointContractHash(endpoint, requestId) {
|
|
106
|
+
return (0, bff_core_namespaceObject.createOperationContractHash)(createEffectOperationContractSource(endpoint), requestId);
|
|
107
|
+
}
|
|
108
|
+
exports.collectEffectEndpoints = __webpack_exports__.collectEffectEndpoints;
|
|
109
|
+
exports.createEffectEndpointContractHash = __webpack_exports__.createEffectEndpointContractHash;
|
|
110
|
+
exports.createEffectOperationContractSource = __webpack_exports__.createEffectOperationContractSource;
|
|
111
|
+
exports.ensureLeadingSlash = __webpack_exports__.ensureLeadingSlash;
|
|
112
|
+
exports.extractHttpApiFromModule = __webpack_exports__.extractHttpApiFromModule;
|
|
113
|
+
exports.getEffectRoutePath = __webpack_exports__.getEffectRoutePath;
|
|
114
|
+
exports.normalizeEffectPrefix = __webpack_exports__.normalizeEffectPrefix;
|
|
115
|
+
exports.resolveEffectApiId = __webpack_exports__.resolveEffectApiId;
|
|
116
|
+
exports.toOperationContractSources = __webpack_exports__.toOperationContractSources;
|
|
117
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
118
|
+
"collectEffectEndpoints",
|
|
119
|
+
"createEffectEndpointContractHash",
|
|
120
|
+
"createEffectOperationContractSource",
|
|
121
|
+
"ensureLeadingSlash",
|
|
122
|
+
"extractHttpApiFromModule",
|
|
123
|
+
"getEffectRoutePath",
|
|
124
|
+
"normalizeEffectPrefix",
|
|
125
|
+
"resolveEffectApiId",
|
|
126
|
+
"toOperationContractSources"
|
|
127
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
128
|
+
Object.defineProperty(exports, '__esModule', {
|
|
129
|
+
value: true
|
|
130
|
+
});
|
|
@@ -100,6 +100,8 @@ var __webpack_exports__ = {};
|
|
|
100
100
|
"Effect",
|
|
101
101
|
"defineEffectRpcBff",
|
|
102
102
|
"createHttpApiHandler",
|
|
103
|
+
"EFFECT_VALIDATOR_AWARE_FACTORY",
|
|
104
|
+
"isValidatorAwareHandlerFactory",
|
|
103
105
|
"Option"
|
|
104
106
|
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>effect_unstable_http__rspack_import_2[__rspack_import_key];
|
|
105
107
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
@@ -115,6 +117,8 @@ var __webpack_exports__ = {};
|
|
|
115
117
|
"Effect",
|
|
116
118
|
"defineEffectRpcBff",
|
|
117
119
|
"createHttpApiHandler",
|
|
120
|
+
"EFFECT_VALIDATOR_AWARE_FACTORY",
|
|
121
|
+
"isValidatorAwareHandlerFactory",
|
|
118
122
|
"Option"
|
|
119
123
|
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>effect_unstable_httpapi__rspack_import_3[__rspack_import_key];
|
|
120
124
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
@@ -130,10 +134,16 @@ var __webpack_exports__ = {};
|
|
|
130
134
|
"Effect",
|
|
131
135
|
"defineEffectRpcBff",
|
|
132
136
|
"createHttpApiHandler",
|
|
137
|
+
"EFFECT_VALIDATOR_AWARE_FACTORY",
|
|
138
|
+
"isValidatorAwareHandlerFactory",
|
|
133
139
|
"Option"
|
|
134
140
|
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>effect_unstable_rpc__rspack_import_4[__rspack_import_key];
|
|
135
141
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
136
142
|
const emptyEffectServiceContext = effect_Context__rspack_import_0.empty();
|
|
143
|
+
const EFFECT_VALIDATOR_AWARE_FACTORY = Symbol.for('modernjs.effect.validatorAware');
|
|
144
|
+
function isValidatorAwareHandlerFactory(factory) {
|
|
145
|
+
return 'function' == typeof factory && true === factory[EFFECT_VALIDATOR_AWARE_FACTORY];
|
|
146
|
+
}
|
|
137
147
|
function normalizeOpenApiPath(pathname) {
|
|
138
148
|
if (!pathname.startsWith('/')) return `/${pathname}`;
|
|
139
149
|
return pathname;
|
|
@@ -389,16 +399,38 @@ var __webpack_exports__ = {};
|
|
|
389
399
|
layer: definition.layer,
|
|
390
400
|
openapi: options?.openapi,
|
|
391
401
|
rpc: mergedRpcOptions,
|
|
392
|
-
dataPlatform: mergeDataPlatformOptions(definition.dataPlatform, options?.dataPlatform)
|
|
402
|
+
dataPlatform: mergeDataPlatformOptions(definition.dataPlatform, options?.dataPlatform),
|
|
403
|
+
validateRequest: options?.validateRequest
|
|
393
404
|
});
|
|
394
405
|
};
|
|
395
|
-
|
|
406
|
+
Object.defineProperty(createHandler, EFFECT_VALIDATOR_AWARE_FACTORY, {
|
|
407
|
+
value: true
|
|
408
|
+
});
|
|
409
|
+
const client = createLoaderMaterializedClientPlaceholder();
|
|
396
410
|
return {
|
|
397
411
|
...definition,
|
|
398
412
|
createHandler,
|
|
399
413
|
client
|
|
400
414
|
};
|
|
401
415
|
}
|
|
416
|
+
const LOADER_CLIENT_IGNORED_KEYS = new Set([
|
|
417
|
+
'then',
|
|
418
|
+
'catch',
|
|
419
|
+
'finally',
|
|
420
|
+
'toJSON',
|
|
421
|
+
'$$typeof'
|
|
422
|
+
]);
|
|
423
|
+
function createLoaderMaterializedClientPlaceholder() {
|
|
424
|
+
const explain = (property)=>{
|
|
425
|
+
throw new Error(`[BFF][Effect] effectBff.client.${String(property)} is not available here: the typed client only exists when this module is imported through the "@api/effect/*" transformed path (the BFF loader replaces it with generated client code). On the server, use HttpApiClient or call the Effect layer directly.`);
|
|
426
|
+
};
|
|
427
|
+
return new Proxy(Object.create(null), {
|
|
428
|
+
get (_target, property) {
|
|
429
|
+
if ('symbol' == typeof property || LOADER_CLIENT_IGNORED_KEYS.has(property)) return;
|
|
430
|
+
return explain(property);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
}
|
|
402
434
|
function defineEffectRpcBff(definition) {
|
|
403
435
|
const createHandler = (options)=>createRpcApiHandler({
|
|
404
436
|
...definition,
|
|
@@ -425,6 +457,8 @@ var __webpack_exports__ = {};
|
|
|
425
457
|
const envelopeHeader = options.dataPlatform?.envelopeHeader || _data_platform__rspack_import_5.DEFAULT_DATA_ENVELOPE_HEADER;
|
|
426
458
|
const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
|
|
427
459
|
const withDataPlatformValidation = async (request, context)=>{
|
|
460
|
+
const policyDenial = options.validateRequest?.(request);
|
|
461
|
+
if (policyDenial) return policyDenial;
|
|
428
462
|
const validationError = validateDataPlatformRequestEnvelope(request, options.dataPlatform);
|
|
429
463
|
if (validationError) return validationError;
|
|
430
464
|
return httpApiHandler.handler(request, context ?? emptyEffectServiceContext);
|
|
@@ -546,7 +580,11 @@ var __webpack_exports__ = {};
|
|
|
546
580
|
const rpcHandler = createRpcApiHandler(options.rpc);
|
|
547
581
|
return {
|
|
548
582
|
handler: async (request, context)=>{
|
|
549
|
-
if (isRpcRequest(request, rpcPath))
|
|
583
|
+
if (isRpcRequest(request, rpcPath)) {
|
|
584
|
+
const policyDenial = options.validateRequest?.(request);
|
|
585
|
+
if (policyDenial) return policyDenial;
|
|
586
|
+
return rpcHandler.handler(request, context ?? emptyEffectServiceContext);
|
|
587
|
+
}
|
|
550
588
|
return handleHttpApiRequest(request);
|
|
551
589
|
},
|
|
552
590
|
dispose: async ()=>{
|
|
@@ -567,10 +605,14 @@ var __webpack_exports__ = {};
|
|
|
567
605
|
Schema: ()=>effect_Schema__rspack_import_9,
|
|
568
606
|
createHttpApiHandler: ()=>createHttpApiHandler,
|
|
569
607
|
defineEffectBff: ()=>defineEffectBff,
|
|
570
|
-
defineEffectRpcBff: ()=>defineEffectRpcBff
|
|
608
|
+
defineEffectRpcBff: ()=>defineEffectRpcBff,
|
|
609
|
+
isValidatorAwareHandlerFactory: ()=>isValidatorAwareHandlerFactory
|
|
610
|
+
}, {
|
|
611
|
+
EFFECT_VALIDATOR_AWARE_FACTORY: EFFECT_VALIDATOR_AWARE_FACTORY
|
|
571
612
|
});
|
|
572
613
|
})();
|
|
573
614
|
exports.Config = __webpack_exports__.Config;
|
|
615
|
+
exports.EFFECT_VALIDATOR_AWARE_FACTORY = __webpack_exports__.EFFECT_VALIDATOR_AWARE_FACTORY;
|
|
574
616
|
exports.Effect = __webpack_exports__.Effect;
|
|
575
617
|
exports.HttpApiBuilder = __webpack_exports__.HttpApiBuilder;
|
|
576
618
|
exports.HttpTraceContext = __webpack_exports__.HttpTraceContext;
|
|
@@ -580,8 +622,10 @@ exports.Schema = __webpack_exports__.Schema;
|
|
|
580
622
|
exports.createHttpApiHandler = __webpack_exports__.createHttpApiHandler;
|
|
581
623
|
exports.defineEffectBff = __webpack_exports__.defineEffectBff;
|
|
582
624
|
exports.defineEffectRpcBff = __webpack_exports__.defineEffectRpcBff;
|
|
625
|
+
exports.isValidatorAwareHandlerFactory = __webpack_exports__.isValidatorAwareHandlerFactory;
|
|
583
626
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
584
627
|
"Config",
|
|
628
|
+
"EFFECT_VALIDATOR_AWARE_FACTORY",
|
|
585
629
|
"Effect",
|
|
586
630
|
"HttpApiBuilder",
|
|
587
631
|
"HttpTraceContext",
|
|
@@ -590,7 +634,8 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
590
634
|
"Schema",
|
|
591
635
|
"createHttpApiHandler",
|
|
592
636
|
"defineEffectBff",
|
|
593
|
-
"defineEffectRpcBff"
|
|
637
|
+
"defineEffectRpcBff",
|
|
638
|
+
"isValidatorAwareHandlerFactory"
|
|
594
639
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
595
640
|
Object.defineProperty(exports, '__esModule', {
|
|
596
641
|
value: true
|
|
@@ -52,10 +52,13 @@ const emptyEffectServiceContext = Context_namespaceObject.empty();
|
|
|
52
52
|
function callEffectBffRequestHandler(handler, request, context) {
|
|
53
53
|
return void 0 === context ? handler(request) : handler(request, context);
|
|
54
54
|
}
|
|
55
|
-
function createLoadedHandler(webHandler) {
|
|
55
|
+
function createLoadedHandler(webHandler, appliesRequestValidator) {
|
|
56
56
|
return {
|
|
57
57
|
handler: (request, context)=>callEffectBffRequestHandler(webHandler.handler, request, context),
|
|
58
|
-
dispose: webHandler.dispose
|
|
58
|
+
dispose: webHandler.dispose,
|
|
59
|
+
...appliesRequestValidator ? {
|
|
60
|
+
appliesRequestValidator: true
|
|
61
|
+
} : {}
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
64
|
function createLoadedHttpApiHandler(webHandler) {
|
|
@@ -64,7 +67,8 @@ function createLoadedHttpApiHandler(webHandler) {
|
|
|
64
67
|
const effectContext = isEffectServiceContext(context) ? context : emptyEffectServiceContext;
|
|
65
68
|
return webHandler.handler(request, effectContext);
|
|
66
69
|
},
|
|
67
|
-
dispose: webHandler.dispose
|
|
70
|
+
dispose: webHandler.dispose,
|
|
71
|
+
appliesRequestValidator: true
|
|
68
72
|
};
|
|
69
73
|
}
|
|
70
74
|
function resolveNormalizedEffectBffModuleHandler(normalizedModule, options = {}) {
|
|
@@ -90,11 +94,15 @@ function resolveNormalizedEffectBffModuleHandler(normalizedModule, options = {})
|
|
|
90
94
|
handler: normalizedModule.handler
|
|
91
95
|
};
|
|
92
96
|
if ('function' == typeof normalizedModule.createHandler) {
|
|
93
|
-
const
|
|
97
|
+
const factory = normalizedModule.createHandler;
|
|
98
|
+
const validatorAware = (0, external_handler_js_namespaceObject.isValidatorAwareHandlerFactory)(factory);
|
|
99
|
+
if (!validatorAware && void 0 !== options.validateRequest) options.onWarning?.('[BFF][Effect] Custom createHandler export detected: it cannot be verified to apply validateRequest (cross-project policy), so the policy is enforced by the adapter middleware on the outer request. Batched calls will be denied at the batch POST (it carries no per-operation contract); export defineEffectBff(...) to get per-batch-item enforcement.');
|
|
100
|
+
const webHandler = factory({
|
|
94
101
|
openapi: options.openapi,
|
|
95
|
-
dataPlatform: options.dataPlatform
|
|
102
|
+
dataPlatform: options.dataPlatform,
|
|
103
|
+
validateRequest: options.validateRequest
|
|
96
104
|
});
|
|
97
|
-
return createLoadedHandler(webHandler);
|
|
105
|
+
return createLoadedHandler(webHandler, validatorAware);
|
|
98
106
|
}
|
|
99
107
|
if (isEffectApiDefinition(normalizedModule)) {
|
|
100
108
|
options.onWarning?.('[BFF][Effect] Detected { api, layer } export without createHandler. Prefer `defineEffectBff(...)` from @modern-js/plugin-bff/server to avoid module instance mismatch.');
|
|
@@ -102,7 +110,8 @@ function resolveNormalizedEffectBffModuleHandler(normalizedModule, options = {})
|
|
|
102
110
|
api: normalizedModule.api,
|
|
103
111
|
layer: normalizedModule.layer,
|
|
104
112
|
openapi: options.openapi,
|
|
105
|
-
dataPlatform: options.dataPlatform
|
|
113
|
+
dataPlatform: options.dataPlatform,
|
|
114
|
+
validateRequest: options.validateRequest
|
|
106
115
|
});
|
|
107
116
|
return createLoadedHttpApiHandler(webHandler);
|
|
108
117
|
}
|
|
@@ -31,7 +31,6 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
31
31
|
createEffectOperationContext: ()=>createEffectOperationContext
|
|
32
32
|
});
|
|
33
33
|
const create_request_namespaceObject = require("@modern-js/create-request");
|
|
34
|
-
const TRACEPARENT_REGEX = /^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/i;
|
|
35
34
|
const readHeader = (headers, header)=>{
|
|
36
35
|
const value = headers.get(header);
|
|
37
36
|
return value && value.length > 0 ? value : void 0;
|
|
@@ -40,17 +39,6 @@ const copyStringField = (target, details, key)=>{
|
|
|
40
39
|
const value = details[key];
|
|
41
40
|
if ('string' == typeof value && value.length > 0) target[key] = value;
|
|
42
41
|
};
|
|
43
|
-
const parseTraceparent = (traceparent)=>{
|
|
44
|
-
if (!traceparent) return;
|
|
45
|
-
const match = traceparent.trim().match(TRACEPARENT_REGEX);
|
|
46
|
-
if (!match) return;
|
|
47
|
-
const [, traceId, spanId] = match;
|
|
48
|
-
if (!traceId || !spanId) return;
|
|
49
|
-
return {
|
|
50
|
-
traceId: traceId.toLowerCase(),
|
|
51
|
-
spanId: spanId.toLowerCase()
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
42
|
const readOperationContextDetails = (request)=>{
|
|
55
43
|
const rawDetails = readHeader(request.headers, create_request_namespaceObject.BFF_OPERATION_CONTEXT_DETAIL_HEADER);
|
|
56
44
|
if (!rawDetails) return {};
|
|
@@ -78,7 +66,7 @@ const createEffectOperationContext = ({ request, path, method })=>{
|
|
|
78
66
|
const parsedTraceparent = details.traceId && details.spanId ? {
|
|
79
67
|
traceId: details.traceId,
|
|
80
68
|
spanId: details.spanId
|
|
81
|
-
} : parseTraceparent(traceparent);
|
|
69
|
+
} : (0, create_request_namespaceObject.parseTraceparent)(traceparent);
|
|
82
70
|
const locale = readHeader(request.headers, create_request_namespaceObject.BFF_LOCALE_HEADER);
|
|
83
71
|
const headerOperationId = readHeader(request.headers, create_request_namespaceObject.BFF_OPERATION_CONTEXT_HEADER);
|
|
84
72
|
return {
|