@bleedingdev/modern-js-plugin-bff 3.2.0-ultramodern.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/cli.js +1 -0
- package/dist/cjs/cli.js +294 -0
- package/dist/cjs/constants.js +48 -0
- package/dist/cjs/index.js +58 -0
- package/dist/cjs/loader.js +106 -0
- package/dist/cjs/runtime/create-request/index.js +48 -0
- package/dist/cjs/runtime/data-platform/index.js +693 -0
- package/dist/cjs/runtime/effect/adapter.js +311 -0
- package/dist/cjs/runtime/effect/context.js +48 -0
- package/dist/cjs/runtime/effect/index.js +608 -0
- package/dist/cjs/runtime/effect-client/index.js +178 -0
- package/dist/cjs/runtime/hono/adapter.js +168 -0
- package/dist/cjs/runtime/hono/index.js +65 -0
- package/dist/cjs/runtime/hono/operators.js +68 -0
- package/dist/cjs/server.js +179 -0
- package/dist/cjs/utils/clientGenerator.js +342 -0
- package/dist/cjs/utils/createHonoRoutes.js +138 -0
- package/dist/cjs/utils/crossProjectApiPlugin.js +118 -0
- package/dist/cjs/utils/effectClientGenerator.js +673 -0
- package/dist/cjs/utils/pluginGenerator.js +73 -0
- package/dist/cjs/utils/runtimeGenerator.js +133 -0
- package/dist/esm/cli.mjs +245 -0
- package/dist/esm/constants.mjs +11 -0
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/loader.mjs +62 -0
- package/dist/esm/runtime/create-request/index.mjs +1 -0
- package/dist/esm/runtime/data-platform/index.mjs +599 -0
- package/dist/esm/runtime/effect/adapter.mjs +267 -0
- package/dist/esm/runtime/effect/context.mjs +11 -0
- package/dist/esm/runtime/effect/index.mjs +438 -0
- package/dist/esm/runtime/effect-client/index.mjs +90 -0
- package/dist/esm/runtime/hono/adapter.mjs +124 -0
- package/dist/esm/runtime/hono/index.mjs +2 -0
- package/dist/esm/runtime/hono/operators.mjs +31 -0
- package/dist/esm/server.mjs +135 -0
- package/dist/esm/utils/clientGenerator.mjs +293 -0
- package/dist/esm/utils/createHonoRoutes.mjs +92 -0
- package/dist/esm/utils/crossProjectApiPlugin.mjs +54 -0
- package/dist/esm/utils/effectClientGenerator.mjs +623 -0
- package/dist/esm/utils/pluginGenerator.mjs +29 -0
- package/dist/esm/utils/runtimeGenerator.mjs +89 -0
- package/dist/esm-node/cli.mjs +249 -0
- package/dist/esm-node/constants.mjs +12 -0
- package/dist/esm-node/index.mjs +2 -0
- package/dist/esm-node/loader.mjs +64 -0
- package/dist/esm-node/runtime/create-request/index.mjs +2 -0
- package/dist/esm-node/runtime/data-platform/index.mjs +600 -0
- package/dist/esm-node/runtime/effect/adapter.mjs +269 -0
- package/dist/esm-node/runtime/effect/context.mjs +12 -0
- package/dist/esm-node/runtime/effect/index.mjs +439 -0
- package/dist/esm-node/runtime/effect-client/index.mjs +91 -0
- package/dist/esm-node/runtime/hono/adapter.mjs +125 -0
- package/dist/esm-node/runtime/hono/index.mjs +3 -0
- package/dist/esm-node/runtime/hono/operators.mjs +32 -0
- package/dist/esm-node/server.mjs +136 -0
- package/dist/esm-node/utils/clientGenerator.mjs +294 -0
- package/dist/esm-node/utils/createHonoRoutes.mjs +93 -0
- package/dist/esm-node/utils/crossProjectApiPlugin.mjs +55 -0
- package/dist/esm-node/utils/effectClientGenerator.mjs +625 -0
- package/dist/esm-node/utils/pluginGenerator.mjs +33 -0
- package/dist/esm-node/utils/runtimeGenerator.mjs +91 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/loader.d.ts +27 -0
- package/dist/types/runtime/create-request/index.d.ts +2 -0
- package/dist/types/runtime/data-platform/index.d.ts +187 -0
- package/dist/types/runtime/effect/adapter.d.ts +22 -0
- package/dist/types/runtime/effect/context.d.ts +8 -0
- package/dist/types/runtime/effect/index.d.ts +171 -0
- package/dist/types/runtime/effect-client/index.d.ts +47 -0
- package/dist/types/runtime/hono/adapter.d.ts +19 -0
- package/dist/types/runtime/hono/index.d.ts +2 -0
- package/dist/types/runtime/hono/operators.d.ts +10 -0
- package/dist/types/server.d.ts +3 -0
- package/dist/types/utils/clientGenerator.d.ts +37 -0
- package/dist/types/utils/createHonoRoutes.d.ts +10 -0
- package/dist/types/utils/crossProjectApiPlugin.d.ts +9 -0
- package/dist/types/utils/effectClientGenerator.d.ts +27 -0
- package/dist/types/utils/pluginGenerator.d.ts +9 -0
- package/dist/types/utils/runtimeGenerator.d.ts +7 -0
- package/docs/data-platform-architecture.md +61 -0
- package/package.json +172 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +10 -0
- package/server.js +1 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import __rslib_shim_module__ from "node:module";
|
|
2
|
+
const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
|
|
3
|
+
import { DEFAULT_OPERATION_VERSION, createOperationEntries, createOperationSchemaHash } from "@modern-js/bff-core";
|
|
4
|
+
import { compatibleRequire, findExists, fs, logger } from "@modern-js/utils";
|
|
5
|
+
import path from "path";
|
|
6
|
+
const JS_OR_TS_EXTS = [
|
|
7
|
+
'.js',
|
|
8
|
+
'.jsx',
|
|
9
|
+
'.ts',
|
|
10
|
+
'.tsx',
|
|
11
|
+
'.mjs',
|
|
12
|
+
'.mts',
|
|
13
|
+
'.cjs',
|
|
14
|
+
'.cts'
|
|
15
|
+
];
|
|
16
|
+
const DEFAULT_REQUEST_CREATOR = '@modern-js/plugin-bff/client';
|
|
17
|
+
const DEFAULT_DATA_PLATFORM_IMPORT = '@modern-js/plugin-bff/data-platform';
|
|
18
|
+
let httpApiRuntimePromise;
|
|
19
|
+
function isRecord(value) {
|
|
20
|
+
return 'object' == typeof value && null !== value;
|
|
21
|
+
}
|
|
22
|
+
function ensureLeadingSlash(pathname) {
|
|
23
|
+
return pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
24
|
+
}
|
|
25
|
+
function normalizePrefix(prefix) {
|
|
26
|
+
if ('/' === prefix) return '';
|
|
27
|
+
return ensureLeadingSlash(prefix || '/api');
|
|
28
|
+
}
|
|
29
|
+
function isAbsoluteUrl(value) {
|
|
30
|
+
try {
|
|
31
|
+
new URL(value);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function resolveBatchEndpoint(prefix, endpoint) {
|
|
38
|
+
const value = endpoint || '/_data/batch';
|
|
39
|
+
if (isAbsoluteUrl(value)) return value;
|
|
40
|
+
const normalizedPrefix = normalizePrefix(prefix);
|
|
41
|
+
const normalizedEndpoint = ensureLeadingSlash(value);
|
|
42
|
+
if (!normalizedPrefix) return normalizedEndpoint;
|
|
43
|
+
if (normalizedEndpoint === normalizedPrefix || normalizedEndpoint.startsWith(`${normalizedPrefix}/`)) return normalizedEndpoint;
|
|
44
|
+
return `${normalizedPrefix}${'/' === normalizedEndpoint ? '' : normalizedEndpoint}`;
|
|
45
|
+
}
|
|
46
|
+
function getRoutePath(prefix, endpointPath) {
|
|
47
|
+
const normalizedPrefix = normalizePrefix(prefix);
|
|
48
|
+
const normalizedEndpointPath = ensureLeadingSlash(endpointPath);
|
|
49
|
+
const finalEndpointPath = '/' === normalizedEndpointPath ? '' : endpointPath;
|
|
50
|
+
if (!normalizedPrefix && !finalEndpointPath) return '/';
|
|
51
|
+
return `${normalizedPrefix}${finalEndpointPath || ''}`;
|
|
52
|
+
}
|
|
53
|
+
function toSafeIdentifier(name) {
|
|
54
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_');
|
|
55
|
+
if (!sanitized) return '_';
|
|
56
|
+
if (/^[0-9]/.test(sanitized)) return `_${sanitized}`;
|
|
57
|
+
return sanitized;
|
|
58
|
+
}
|
|
59
|
+
function getPackageName(appDir) {
|
|
60
|
+
try {
|
|
61
|
+
const packageJsonPath = path.resolve(appDir, './package.json');
|
|
62
|
+
const packageJson = fs.readJSONSync(packageJsonPath);
|
|
63
|
+
return packageJson.name;
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function getHttpApiRuntime() {
|
|
69
|
+
if (!httpApiRuntimePromise) httpApiRuntimePromise = (async ()=>{
|
|
70
|
+
let mod;
|
|
71
|
+
try {
|
|
72
|
+
mod = await compatibleRequire('effect/unstable/httpapi', false);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
+
if (!message.includes("Cannot find module 'effect/unstable/httpapi'")) throw error;
|
|
76
|
+
const effectPackageJson = require.resolve('effect/package.json');
|
|
77
|
+
const effectHttpApiRuntimePath = path.join(path.dirname(effectPackageJson), 'dist', 'unstable', 'httpapi', 'index.js');
|
|
78
|
+
mod = await compatibleRequire(effectHttpApiRuntimePath, false);
|
|
79
|
+
}
|
|
80
|
+
if (isRecord(mod) && isRecord(mod.HttpApi)) {
|
|
81
|
+
const maybeHttpApi = mod.HttpApi;
|
|
82
|
+
if ('function' == typeof maybeHttpApi.isHttpApi && 'function' == typeof maybeHttpApi.reflect) return maybeHttpApi;
|
|
83
|
+
}
|
|
84
|
+
throw new Error('[BFF][Effect] Unable to resolve HttpApi runtime from effect/unstable/httpapi.');
|
|
85
|
+
})();
|
|
86
|
+
return httpApiRuntimePromise;
|
|
87
|
+
}
|
|
88
|
+
function resolveApiId(api) {
|
|
89
|
+
const fallback = 'EffectHttpApi';
|
|
90
|
+
const maybeApi = api;
|
|
91
|
+
if ('identifier' in maybeApi && 'string' == typeof maybeApi.identifier && maybeApi.identifier) return maybeApi.identifier;
|
|
92
|
+
return fallback;
|
|
93
|
+
}
|
|
94
|
+
function collectEffectEndpoints(httpApiRuntime, api, prefix) {
|
|
95
|
+
const endpoints = [];
|
|
96
|
+
const apiId = resolveApiId(api);
|
|
97
|
+
httpApiRuntime.reflect(api, {
|
|
98
|
+
onGroup: ()=>{},
|
|
99
|
+
onEndpoint: ({ group, endpoint })=>{
|
|
100
|
+
endpoints.push({
|
|
101
|
+
apiId,
|
|
102
|
+
groupName: String(group.identifier),
|
|
103
|
+
endpointName: String(endpoint.name),
|
|
104
|
+
method: String(endpoint.method).toUpperCase(),
|
|
105
|
+
routePath: getRoutePath(prefix, String(endpoint.path))
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return endpoints.sort((a, b)=>{
|
|
110
|
+
if (a.groupName === b.groupName) return a.endpointName.localeCompare(b.endpointName);
|
|
111
|
+
return a.groupName.localeCompare(b.groupName);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function loadEffectApi(resourcePath) {
|
|
115
|
+
const httpApiRuntime = await getHttpApiRuntime();
|
|
116
|
+
const mod = await compatibleRequire(resourcePath, false);
|
|
117
|
+
if (isRecord(mod) && httpApiRuntime.isHttpApi(mod.api)) return mod.api;
|
|
118
|
+
if (isRecord(mod) && isRecord(mod.default) && httpApiRuntime.isHttpApi(mod.default.api)) return mod.default.api;
|
|
119
|
+
if (isRecord(mod) && 'function' == typeof mod.default && 0 === mod.default.length) {
|
|
120
|
+
const output = await mod.default();
|
|
121
|
+
if (isRecord(output) && httpApiRuntime.isHttpApi(output.api)) return output.api;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function renderEffectClientCode(endpoints, options) {
|
|
126
|
+
const senderDeclarations = [];
|
|
127
|
+
const operationDeclarations = [];
|
|
128
|
+
const callerDeclarations = [];
|
|
129
|
+
const groupedCallers = {};
|
|
130
|
+
const groupedOperations = {};
|
|
131
|
+
const requestCreator = options.requestCreator || DEFAULT_REQUEST_CREATOR;
|
|
132
|
+
const dataPlatformImport = DEFAULT_DATA_PLATFORM_IMPORT;
|
|
133
|
+
const httpMethodDecider = options.httpMethodDecider || 'functionName';
|
|
134
|
+
const portCode = 'server' === options.target ? `process.env.PORT || ${String(options.port)}` : String(options.port);
|
|
135
|
+
const packageName = getPackageName(options.appDir);
|
|
136
|
+
const dataPlatformAppNamespace = packageName || 'unknown-app';
|
|
137
|
+
const requestId = 'bundle' === options.target ? packageName || process.env.npm_package_name : void 0;
|
|
138
|
+
const normalizedRequestId = requestId || 'default';
|
|
139
|
+
const operationVersion = 'number' == typeof DEFAULT_OPERATION_VERSION ? DEFAULT_OPERATION_VERSION : 1;
|
|
140
|
+
const schemaHash = createOperationSchemaHash(createOperationEntries(endpoints.map((endpoint)=>({
|
|
141
|
+
name: endpoint.endpointName,
|
|
142
|
+
httpMethod: endpoint.method,
|
|
143
|
+
routePath: endpoint.routePath
|
|
144
|
+
}))), normalizedRequestId);
|
|
145
|
+
const batchConfig = options.dataPlatformBatch;
|
|
146
|
+
const batchEndpoint = resolveBatchEndpoint(options.prefix, batchConfig?.endpoint);
|
|
147
|
+
const batchConfigCode = JSON.stringify({
|
|
148
|
+
enabled: batchConfig?.enabled ?? true,
|
|
149
|
+
endpoint: batchEndpoint,
|
|
150
|
+
flushIntervalMs: batchConfig?.flushIntervalMs ?? 8,
|
|
151
|
+
maxBatchSize: batchConfig?.maxBatchSize ?? 16,
|
|
152
|
+
maxBatchBytes: batchConfig?.maxBatchBytes ?? 65536,
|
|
153
|
+
requestTimeoutMs: batchConfig?.requestTimeoutMs ?? 10000,
|
|
154
|
+
allowedMethods: batchConfig?.allowedMethods && batchConfig.allowedMethods.length > 0 ? batchConfig.allowedMethods : [
|
|
155
|
+
'GET'
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
endpoints.forEach((endpoint, index)=>{
|
|
159
|
+
const senderVar = `__sender_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
|
|
160
|
+
const callVar = `__call_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
|
|
161
|
+
const operationVar = `__operation_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
|
|
162
|
+
const operationId = `${endpoint.method}:${endpoint.routePath}`;
|
|
163
|
+
const operationContextCode = JSON.stringify({
|
|
164
|
+
operationId,
|
|
165
|
+
routePath: endpoint.routePath,
|
|
166
|
+
method: endpoint.method,
|
|
167
|
+
schemaHash,
|
|
168
|
+
operationVersion
|
|
169
|
+
});
|
|
170
|
+
const createRequestOptions = `{
|
|
171
|
+
path: ${JSON.stringify(endpoint.routePath)},
|
|
172
|
+
method: ${JSON.stringify(endpoint.method)},
|
|
173
|
+
port: ${portCode},
|
|
174
|
+
operationContext: ${operationContextCode},
|
|
175
|
+
httpMethodDecider: ${JSON.stringify(httpMethodDecider)}${requestId ? `, requestId: ${JSON.stringify(requestId)}` : ''}
|
|
176
|
+
}`.replace(/\n\s*/g, '');
|
|
177
|
+
senderDeclarations.push(`const ${senderVar} = createRequest(${createRequestOptions});`);
|
|
178
|
+
operationDeclarations.push(`const ${operationVar} = ${JSON.stringify({
|
|
179
|
+
appNamespace: dataPlatformAppNamespace,
|
|
180
|
+
apiId: endpoint.apiId,
|
|
181
|
+
group: endpoint.groupName,
|
|
182
|
+
endpoint: endpoint.endpointName,
|
|
183
|
+
operationId,
|
|
184
|
+
routePath: endpoint.routePath,
|
|
185
|
+
method: endpoint.method,
|
|
186
|
+
operationVersion,
|
|
187
|
+
schemaHash,
|
|
188
|
+
version: operationVersion
|
|
189
|
+
})};`);
|
|
190
|
+
callerDeclarations.push(`const ${callVar} = (request = {}) => ${senderVar}(__prepareEffectRequest(${JSON.stringify(endpoint.method)}, ${JSON.stringify(endpoint.routePath)}, ${operationVar}, request));`);
|
|
191
|
+
groupedCallers[endpoint.groupName] ??= [];
|
|
192
|
+
groupedCallers[endpoint.groupName].push({
|
|
193
|
+
endpointName: endpoint.endpointName,
|
|
194
|
+
callVar
|
|
195
|
+
});
|
|
196
|
+
groupedOperations[endpoint.groupName] ??= [];
|
|
197
|
+
groupedOperations[endpoint.groupName].push({
|
|
198
|
+
endpointName: endpoint.endpointName,
|
|
199
|
+
operationVar
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
const groupObjectEntries = Object.entries(groupedCallers).map(([groupName, groupCallers])=>{
|
|
203
|
+
const endpointEntries = groupCallers.map((caller)=>`${JSON.stringify(caller.endpointName)}: ${caller.callVar}`).join(', ');
|
|
204
|
+
return `${JSON.stringify(groupName)}: { ${endpointEntries} }`;
|
|
205
|
+
});
|
|
206
|
+
const clientObject = groupObjectEntries.length ? `{
|
|
207
|
+
${groupObjectEntries.join(',\n ')}
|
|
208
|
+
}` : '{}';
|
|
209
|
+
const operationManifestEntries = Object.entries(groupedOperations).map(([groupName, groupOperations])=>{
|
|
210
|
+
const endpointEntries = groupOperations.map((operation)=>`${JSON.stringify(operation.endpointName)}: ${operation.operationVar}`).join(', ');
|
|
211
|
+
return `${JSON.stringify(groupName)}: { ${endpointEntries} }`;
|
|
212
|
+
});
|
|
213
|
+
const operationManifestObject = operationManifestEntries.length ? `{
|
|
214
|
+
${operationManifestEntries.join(',\n ')}
|
|
215
|
+
}` : '{}';
|
|
216
|
+
return `import * as __requestRuntime from ${JSON.stringify(requestCreator)};
|
|
217
|
+
import {
|
|
218
|
+
createDataBatchTransport,
|
|
219
|
+
DEFAULT_DATA_BATCH_HEADER,
|
|
220
|
+
DEFAULT_DATA_ENVELOPE_HEADER,
|
|
221
|
+
createRequestEnvelope,
|
|
222
|
+
encodeRequestEnvelopeHeader,
|
|
223
|
+
} from ${JSON.stringify(dataPlatformImport)};
|
|
224
|
+
|
|
225
|
+
const createRequest = __requestRuntime.createRequest;
|
|
226
|
+
const __configureRequest =
|
|
227
|
+
typeof __requestRuntime.configure === 'function'
|
|
228
|
+
? __requestRuntime.configure
|
|
229
|
+
: undefined;
|
|
230
|
+
const __createRequestContextHeaders =
|
|
231
|
+
typeof __requestRuntime.createRequestContextHeaders === 'function'
|
|
232
|
+
? __requestRuntime.createRequestContextHeaders
|
|
233
|
+
: undefined;
|
|
234
|
+
|
|
235
|
+
const __METHODS_WITHOUT_BODY = new Set(['GET', 'DELETE', 'HEAD', 'OPTIONS']);
|
|
236
|
+
const __DATA_REQUEST_MODES = new Set(['cache-first', 'stale-while-revalidate', 'network-only']);
|
|
237
|
+
const __DATA_MUTATION_MODES = new Set(['optimistic', 'pessimistic', 'fire-and-forget']);
|
|
238
|
+
const __DEFAULT_APP_NAMESPACE = ${JSON.stringify(dataPlatformAppNamespace)};
|
|
239
|
+
const __DEFAULT_ORIGIN = 'http://localhost:${String(options.port)}';
|
|
240
|
+
const __DEFAULT_BATCH_CONFIG = ${batchConfigCode};
|
|
241
|
+
const __REQUEST_ID = ${requestId ? JSON.stringify(requestId) : 'undefined'};
|
|
242
|
+
const __RUNTIME_FETCH =
|
|
243
|
+
typeof fetch === 'function' ? fetch.bind(globalThis) : undefined;
|
|
244
|
+
|
|
245
|
+
if (__REQUEST_ID && __configureRequest) {
|
|
246
|
+
const __configurePayload = {
|
|
247
|
+
requestId: __REQUEST_ID,
|
|
248
|
+
requireEnvelope: true,
|
|
249
|
+
identityBinding: {
|
|
250
|
+
enabled: true,
|
|
251
|
+
strict: true,
|
|
252
|
+
},
|
|
253
|
+
operationContract: {
|
|
254
|
+
enabled: true,
|
|
255
|
+
strict: true,
|
|
256
|
+
requireSchemaHash: true,
|
|
257
|
+
requireOperationVersion: true,
|
|
258
|
+
},
|
|
259
|
+
setDomain: () => {
|
|
260
|
+
if (
|
|
261
|
+
typeof window !== 'undefined' &&
|
|
262
|
+
window.location &&
|
|
263
|
+
typeof window.location.origin === 'string' &&
|
|
264
|
+
window.location.origin
|
|
265
|
+
) {
|
|
266
|
+
return window.location.origin;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (
|
|
270
|
+
typeof globalThis !== 'undefined' &&
|
|
271
|
+
globalThis.location &&
|
|
272
|
+
typeof globalThis.location.origin === 'string' &&
|
|
273
|
+
globalThis.location.origin
|
|
274
|
+
) {
|
|
275
|
+
return globalThis.location.origin;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return __DEFAULT_ORIGIN;
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
if (__DEFAULT_BATCH_CONFIG.enabled !== false && __RUNTIME_FETCH) {
|
|
283
|
+
__configurePayload.request = createDataBatchTransport({
|
|
284
|
+
fetch: __RUNTIME_FETCH,
|
|
285
|
+
endpoint: __DEFAULT_BATCH_CONFIG.endpoint,
|
|
286
|
+
flushIntervalMs: __DEFAULT_BATCH_CONFIG.flushIntervalMs,
|
|
287
|
+
maxBatchSize: __DEFAULT_BATCH_CONFIG.maxBatchSize,
|
|
288
|
+
maxBatchBytes: __DEFAULT_BATCH_CONFIG.maxBatchBytes,
|
|
289
|
+
requestTimeoutMs: __DEFAULT_BATCH_CONFIG.requestTimeoutMs,
|
|
290
|
+
allowedMethods: __DEFAULT_BATCH_CONFIG.allowedMethods,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
__configureRequest(__configurePayload);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const __isRecord = value => typeof value === 'object' && value !== null;
|
|
298
|
+
const __stringOrUndefined = value =>
|
|
299
|
+
typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
300
|
+
const __isDataRequestMode = value =>
|
|
301
|
+
typeof value === 'string' && __DATA_REQUEST_MODES.has(value);
|
|
302
|
+
const __isDataMutationMode = value =>
|
|
303
|
+
typeof value === 'string' && __DATA_MUTATION_MODES.has(value);
|
|
304
|
+
const __normalizeOrigin = value => {
|
|
305
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
return new URL(value).origin;
|
|
310
|
+
} catch {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const __normalizeRequest = (method, request = {}) => {
|
|
316
|
+
if (!__isRecord(request)) {
|
|
317
|
+
return {};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const payload = { ...request };
|
|
321
|
+
|
|
322
|
+
if (__isRecord(request.path) && !__isRecord(payload.params)) {
|
|
323
|
+
payload.params = request.path;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (__isRecord(request.urlParams) && !__isRecord(payload.query)) {
|
|
327
|
+
payload.query = request.urlParams;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (__isRecord(request.headers) && !__isRecord(payload.headers)) {
|
|
331
|
+
payload.headers = request.headers;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if ('payload' in request && request.payload !== undefined) {
|
|
335
|
+
if (request.payload instanceof FormData && !('formData' in payload)) {
|
|
336
|
+
payload.formData = request.payload;
|
|
337
|
+
} else if (__METHODS_WITHOUT_BODY.has(method)) {
|
|
338
|
+
if (__isRecord(request.payload)) {
|
|
339
|
+
payload.query = __isRecord(payload.query)
|
|
340
|
+
? { ...payload.query, ...request.payload }
|
|
341
|
+
: request.payload;
|
|
342
|
+
} else if (!('body' in payload)) {
|
|
343
|
+
payload.body = request.payload;
|
|
344
|
+
}
|
|
345
|
+
} else if (__isRecord(request.payload) && !('data' in payload)) {
|
|
346
|
+
payload.data = request.payload;
|
|
347
|
+
} else if (!('body' in payload)) {
|
|
348
|
+
payload.body = request.payload;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return payload;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const __resolveOrigin = () => {
|
|
356
|
+
if (
|
|
357
|
+
typeof window !== 'undefined' &&
|
|
358
|
+
window.location &&
|
|
359
|
+
typeof window.location.origin === 'string' &&
|
|
360
|
+
window.location.origin
|
|
361
|
+
) {
|
|
362
|
+
return window.location.origin;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (
|
|
366
|
+
typeof globalThis !== 'undefined' &&
|
|
367
|
+
globalThis.location &&
|
|
368
|
+
typeof globalThis.location.origin === 'string' &&
|
|
369
|
+
globalThis.location.origin
|
|
370
|
+
) {
|
|
371
|
+
return globalThis.location.origin;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return __DEFAULT_ORIGIN;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const __resolveTargetOrigin = dataPlatform => {
|
|
378
|
+
const explicitTargetOrigin =
|
|
379
|
+
__stringOrUndefined(dataPlatform.targetOrigin) ||
|
|
380
|
+
__stringOrUndefined(dataPlatform.endpointOrigin);
|
|
381
|
+
if (explicitTargetOrigin) {
|
|
382
|
+
return explicitTargetOrigin;
|
|
383
|
+
}
|
|
384
|
+
return __DEFAULT_ORIGIN;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const __shouldAttachEnvelopeHeader = dataPlatform => {
|
|
388
|
+
if (dataPlatform.allowCrossOriginEnvelope === true) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
const currentOrigin = __normalizeOrigin(__resolveOrigin());
|
|
392
|
+
const targetOrigin = __normalizeOrigin(__resolveTargetOrigin(dataPlatform));
|
|
393
|
+
if (!currentOrigin || !targetOrigin) {
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
return currentOrigin === targetOrigin;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const __toEnvelopeInput = normalizedRequest => {
|
|
400
|
+
if (!__isRecord(normalizedRequest)) {
|
|
401
|
+
return {};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const payload = {};
|
|
405
|
+
if (__isRecord(normalizedRequest.params)) {
|
|
406
|
+
payload.path = normalizedRequest.params;
|
|
407
|
+
}
|
|
408
|
+
if (__isRecord(normalizedRequest.query)) {
|
|
409
|
+
payload.query = normalizedRequest.query;
|
|
410
|
+
}
|
|
411
|
+
if ('data' in normalizedRequest && normalizedRequest.data !== undefined) {
|
|
412
|
+
payload.data = normalizedRequest.data;
|
|
413
|
+
}
|
|
414
|
+
if ('body' in normalizedRequest && normalizedRequest.body !== undefined) {
|
|
415
|
+
payload.body = normalizedRequest.body;
|
|
416
|
+
}
|
|
417
|
+
if (
|
|
418
|
+
typeof FormData !== 'undefined' &&
|
|
419
|
+
normalizedRequest.formData instanceof FormData
|
|
420
|
+
) {
|
|
421
|
+
payload.formData = Array.from(normalizedRequest.formData.entries()).map(
|
|
422
|
+
([key, value]) => [key, String(value)],
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
if (
|
|
426
|
+
typeof URLSearchParams !== 'undefined' &&
|
|
427
|
+
normalizedRequest.formUrlencoded instanceof URLSearchParams
|
|
428
|
+
) {
|
|
429
|
+
payload.formUrlencoded = normalizedRequest.formUrlencoded.toString();
|
|
430
|
+
}
|
|
431
|
+
return payload;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const createEffectRequestContext = requestContext => {
|
|
435
|
+
if (!__isRecord(requestContext)) {
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const headers = __createRequestContextHeaders
|
|
440
|
+
? __createRequestContextHeaders(requestContext)
|
|
441
|
+
: {};
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
...requestContext,
|
|
445
|
+
headers,
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const __applyRequestContext = (normalizedRequest, request = {}) => {
|
|
450
|
+
if (!__isRecord(request) || !__isRecord(request.requestContext)) {
|
|
451
|
+
return normalizedRequest;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const requestContext = createEffectRequestContext(request.requestContext);
|
|
455
|
+
const requestHeaders = __isRecord(requestContext.headers)
|
|
456
|
+
? requestContext.headers
|
|
457
|
+
: {};
|
|
458
|
+
|
|
459
|
+
if (Object.keys(requestHeaders).length === 0) {
|
|
460
|
+
return normalizedRequest;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
...normalizedRequest,
|
|
465
|
+
headers: {
|
|
466
|
+
...requestHeaders,
|
|
467
|
+
...(__isRecord(normalizedRequest.headers) ? normalizedRequest.headers : {}),
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const __prepareEffectRequest = (method, routePath, operation, request = {}) => {
|
|
473
|
+
const normalizedRequest = __applyRequestContext(
|
|
474
|
+
__normalizeRequest(method, request),
|
|
475
|
+
request,
|
|
476
|
+
);
|
|
477
|
+
const dataPlatform = __isRecord(request) && __isRecord(request.dataPlatform)
|
|
478
|
+
? request.dataPlatform
|
|
479
|
+
: {};
|
|
480
|
+
const strictEnvelope =
|
|
481
|
+
dataPlatform.requireEnvelope === true || dataPlatform.strict === true;
|
|
482
|
+
|
|
483
|
+
if (!strictEnvelope && !__shouldAttachEnvelopeHeader(dataPlatform)) {
|
|
484
|
+
return normalizedRequest;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
const namespace =
|
|
489
|
+
__stringOrUndefined(dataPlatform.appNamespace) || __DEFAULT_APP_NAMESPACE;
|
|
490
|
+
const origin = __stringOrUndefined(dataPlatform.origin) || __resolveOrigin();
|
|
491
|
+
const envelope = createRequestEnvelope({
|
|
492
|
+
operation: {
|
|
493
|
+
...operation,
|
|
494
|
+
appNamespace: namespace,
|
|
495
|
+
},
|
|
496
|
+
scope: {
|
|
497
|
+
appNamespace: namespace,
|
|
498
|
+
origin,
|
|
499
|
+
tenantId: __stringOrUndefined(dataPlatform.tenantId),
|
|
500
|
+
userId: __stringOrUndefined(dataPlatform.userId),
|
|
501
|
+
sessionId: __stringOrUndefined(dataPlatform.sessionId),
|
|
502
|
+
},
|
|
503
|
+
requestInput: {
|
|
504
|
+
method,
|
|
505
|
+
routePath,
|
|
506
|
+
payload: __toEnvelopeInput(normalizedRequest),
|
|
507
|
+
},
|
|
508
|
+
requestMode: __isDataRequestMode(dataPlatform.requestMode)
|
|
509
|
+
? dataPlatform.requestMode
|
|
510
|
+
: undefined,
|
|
511
|
+
mutationMode: __isDataMutationMode(dataPlatform.mutationMode)
|
|
512
|
+
? dataPlatform.mutationMode
|
|
513
|
+
: undefined,
|
|
514
|
+
selectionPlan: __isRecord(dataPlatform.selectionPlan)
|
|
515
|
+
? dataPlatform.selectionPlan
|
|
516
|
+
: undefined,
|
|
517
|
+
traceContext: __isRecord(dataPlatform.traceContext)
|
|
518
|
+
? dataPlatform.traceContext
|
|
519
|
+
: undefined,
|
|
520
|
+
requireTraceContext: dataPlatform.requireTraceContext === true,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
const headerName =
|
|
524
|
+
__stringOrUndefined(dataPlatform.envelopeHeader) ||
|
|
525
|
+
DEFAULT_DATA_ENVELOPE_HEADER;
|
|
526
|
+
const headers = __isRecord(normalizedRequest.headers)
|
|
527
|
+
? { ...normalizedRequest.headers }
|
|
528
|
+
: {};
|
|
529
|
+
|
|
530
|
+
if (dataPlatform.batch === false) {
|
|
531
|
+
headers[DEFAULT_DATA_BATCH_HEADER] = 'off';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
headers[headerName] = encodeRequestEnvelopeHeader(envelope);
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
...normalizedRequest,
|
|
538
|
+
headers,
|
|
539
|
+
};
|
|
540
|
+
} catch (error) {
|
|
541
|
+
if (strictEnvelope) {
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
544
|
+
return normalizedRequest;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
${senderDeclarations.join('\n')}
|
|
549
|
+
${operationDeclarations.join('\n')}
|
|
550
|
+
${callerDeclarations.join('\n')}
|
|
551
|
+
|
|
552
|
+
const client = ${clientObject};
|
|
553
|
+
const operationManifest = ${operationManifestObject};
|
|
554
|
+
const effectBffModule = {
|
|
555
|
+
client,
|
|
556
|
+
operationManifest,
|
|
557
|
+
createEffectRequestContext,
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
export { client, createEffectRequestContext, operationManifest };
|
|
561
|
+
export default effectBffModule;
|
|
562
|
+
`;
|
|
563
|
+
}
|
|
564
|
+
function renderEffectClientDeclaration() {
|
|
565
|
+
return `export type EffectClientOperation = (
|
|
566
|
+
request?: unknown,
|
|
567
|
+
) => Promise<unknown>;
|
|
568
|
+
export type EffectClientGroup = Record<string, EffectClientOperation>;
|
|
569
|
+
export type EffectClient = Record<string, EffectClientGroup>;
|
|
570
|
+
export type EffectOperationDescriptor = {
|
|
571
|
+
appNamespace: string;
|
|
572
|
+
apiId: string;
|
|
573
|
+
group: string;
|
|
574
|
+
endpoint: string;
|
|
575
|
+
operationId: string;
|
|
576
|
+
routePath: string;
|
|
577
|
+
method: string;
|
|
578
|
+
operationVersion: number;
|
|
579
|
+
schemaHash: string;
|
|
580
|
+
version: number;
|
|
581
|
+
};
|
|
582
|
+
export type EffectOperationManifest = Record<
|
|
583
|
+
string,
|
|
584
|
+
Record<string, EffectOperationDescriptor>
|
|
585
|
+
>;
|
|
586
|
+
export type EffectRequestContext = {
|
|
587
|
+
headers?: Record<string, string>;
|
|
588
|
+
locale?: string;
|
|
589
|
+
traceparent?: string;
|
|
590
|
+
traceId?: string;
|
|
591
|
+
spanId?: string;
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
export declare const client: EffectClient;
|
|
595
|
+
export declare const createEffectRequestContext: (
|
|
596
|
+
requestContext: Record<string, unknown>,
|
|
597
|
+
) => EffectRequestContext;
|
|
598
|
+
export declare const operationManifest: EffectOperationManifest;
|
|
599
|
+
declare const effectBffModule: {
|
|
600
|
+
client: EffectClient;
|
|
601
|
+
createEffectRequestContext: typeof createEffectRequestContext;
|
|
602
|
+
operationManifest: EffectOperationManifest;
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
export default effectBffModule;
|
|
606
|
+
`;
|
|
607
|
+
}
|
|
608
|
+
async function generateEffectClientCode(options) {
|
|
609
|
+
const api = await loadEffectApi(options.resourcePath);
|
|
610
|
+
if (!api) {
|
|
611
|
+
logger.warn(`[BFF][Effect] Failed to generate client for ${options.resourcePath}: unable to resolve exported HttpApi.`);
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
const httpApiRuntime = await getHttpApiRuntime();
|
|
615
|
+
const endpoints = collectEffectEndpoints(httpApiRuntime, api, options.prefix);
|
|
616
|
+
return renderEffectClientCode(endpoints, options);
|
|
617
|
+
}
|
|
618
|
+
function resolveEffectEntryFile(options) {
|
|
619
|
+
const { appDir, apiDir, effectEntry } = options;
|
|
620
|
+
const defaultEntry = path.resolve(apiDir, 'effect', 'index');
|
|
621
|
+
const entryWithoutExt = effectEntry ? path.isAbsolute(effectEntry) ? effectEntry : path.resolve(appDir, effectEntry) : defaultEntry;
|
|
622
|
+
if (path.extname(entryWithoutExt)) return fs.existsSync(entryWithoutExt) ? entryWithoutExt : void 0;
|
|
623
|
+
return findExists(JS_OR_TS_EXTS.map((ext)=>`${entryWithoutExt}${ext}`));
|
|
624
|
+
}
|
|
625
|
+
export { generateEffectClientCode, renderEffectClientDeclaration, resolveEffectEntryFile };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { fs, logger, normalizeToPosixPath } from "@modern-js/utils";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { API_DIR, DIST_DIR, LAMBDA_DIR, PACKAGE_NAME, PREFIX, RUNTIME_FRAMEWORK } from "./crossProjectApiPlugin.mjs";
|
|
5
|
+
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
6
|
+
import { dirname as __rspack_dirname } from "node:path";
|
|
7
|
+
var pluginGenerator_dirname = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
8
|
+
function replaceContent(source, packageName, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework) {
|
|
9
|
+
const updatedSource = source.replace(new RegExp(PACKAGE_NAME, 'g'), packageName).replace(new RegExp(PREFIX, 'g'), prefix).replace(new RegExp(DIST_DIR, 'g'), normalizeToPosixPath(relativeDistPath)).replace(new RegExp(API_DIR, 'g'), normalizeToPosixPath(relativeApiPath)).replace(new RegExp(LAMBDA_DIR, 'g'), normalizeToPosixPath(relativeLambdaPath)).replace(new RegExp(RUNTIME_FRAMEWORK, 'g'), runtimeFramework);
|
|
10
|
+
return updatedSource;
|
|
11
|
+
}
|
|
12
|
+
async function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework }) {
|
|
13
|
+
try {
|
|
14
|
+
const packageContent = await fs.readFile(path.resolve(appDirectory, './package.json'), 'utf8');
|
|
15
|
+
const packageJson = JSON.parse(packageContent);
|
|
16
|
+
const pluginDir = path.resolve(appDirectory, `./${relativeDistPath}`, 'plugin');
|
|
17
|
+
const pluginPath = path.join(pluginDir, 'index.js');
|
|
18
|
+
const pluginTemplate = await fs.readFile(path.resolve(pluginGenerator_dirname, 'crossProjectApiPlugin.js'), 'utf8');
|
|
19
|
+
const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework);
|
|
20
|
+
await fs.ensureFile(pluginPath);
|
|
21
|
+
await fs.writeFile(pluginPath, updatedPlugin);
|
|
22
|
+
const typeContent = `import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
23
|
+
export declare const crossProjectApiPlugin: () => CliPlugin<AppTools>`;
|
|
24
|
+
const pluginTypePath = path.join(pluginDir, 'index.d.ts');
|
|
25
|
+
await fs.ensureFile(pluginTypePath);
|
|
26
|
+
await fs.writeFile(pluginTypePath, typeContent);
|
|
27
|
+
logger.info('Api plugin generate succeed');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error('Api plugin generate failed:', error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const utils_pluginGenerator = pluginGenerator;
|
|
33
|
+
export default utils_pluginGenerator;
|