@bleedingdev/modern-js-bff-core 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/cjs/client/generateClient.js +67 -138
  2. package/dist/cjs/index.js +24 -114
  3. package/dist/cjs/router/index.js +7 -5
  4. package/dist/cjs/utils/index.js +11 -21
  5. package/dist/esm/client/generateClient.mjs +65 -139
  6. package/dist/esm/index.mjs +1 -5
  7. package/dist/esm/router/index.mjs +7 -5
  8. package/dist/esm/utils/index.mjs +0 -1
  9. package/dist/esm-node/client/generateClient.mjs +65 -139
  10. package/dist/esm-node/index.mjs +1 -5
  11. package/dist/esm-node/router/index.mjs +7 -5
  12. package/dist/esm-node/utils/index.mjs +0 -1
  13. package/dist/types/client/generateClient.d.ts +24 -1
  14. package/dist/types/index.d.ts +1 -5
  15. package/dist/types/utils/index.d.ts +0 -1
  16. package/package.json +10 -19
  17. package/dist/cjs/adapter-kit/index.js +0 -144
  18. package/dist/cjs/adapter-kit/parity-scenarios/cross-project-denial.js +0 -55
  19. package/dist/cjs/adapter-kit/parity-scenarios/envelope.js +0 -128
  20. package/dist/cjs/adapter-kit/parity-scenarios/operation-context.js +0 -152
  21. package/dist/cjs/adapter-kit/parity-scenarios/schema.js +0 -109
  22. package/dist/cjs/adapter-kit/parity-scenarios/shared.js +0 -71
  23. package/dist/cjs/adapter-kit/parity.js +0 -204
  24. package/dist/cjs/security/crossProjectPolicy.js +0 -309
  25. package/dist/cjs/security/operationContracts.js +0 -183
  26. package/dist/cjs/security/resolveCrossProjectPolicy.js +0 -65
  27. package/dist/cjs/utils/alias.js +0 -112
  28. package/dist/esm/adapter-kit/index.mjs +0 -79
  29. package/dist/esm/adapter-kit/parity-scenarios/cross-project-denial.mjs +0 -17
  30. package/dist/esm/adapter-kit/parity-scenarios/envelope.mjs +0 -90
  31. package/dist/esm/adapter-kit/parity-scenarios/operation-context.mjs +0 -111
  32. package/dist/esm/adapter-kit/parity-scenarios/schema.mjs +0 -71
  33. package/dist/esm/adapter-kit/parity-scenarios/shared.mjs +0 -21
  34. package/dist/esm/adapter-kit/parity.mjs +0 -148
  35. package/dist/esm/security/crossProjectPolicy.mjs +0 -259
  36. package/dist/esm/security/operationContracts.mjs +0 -127
  37. package/dist/esm/security/resolveCrossProjectPolicy.mjs +0 -27
  38. package/dist/esm/utils/alias.mjs +0 -57
  39. package/dist/esm-node/adapter-kit/index.mjs +0 -80
  40. package/dist/esm-node/adapter-kit/parity-scenarios/cross-project-denial.mjs +0 -18
  41. package/dist/esm-node/adapter-kit/parity-scenarios/envelope.mjs +0 -91
  42. package/dist/esm-node/adapter-kit/parity-scenarios/operation-context.mjs +0 -112
  43. package/dist/esm-node/adapter-kit/parity-scenarios/schema.mjs +0 -72
  44. package/dist/esm-node/adapter-kit/parity-scenarios/shared.mjs +0 -22
  45. package/dist/esm-node/adapter-kit/parity.mjs +0 -149
  46. package/dist/esm-node/security/crossProjectPolicy.mjs +0 -260
  47. package/dist/esm-node/security/operationContracts.mjs +0 -130
  48. package/dist/esm-node/security/resolveCrossProjectPolicy.mjs +0 -28
  49. package/dist/esm-node/utils/alias.mjs +0 -58
  50. package/dist/types/adapter-kit/index.d.ts +0 -90
  51. package/dist/types/adapter-kit/parity-scenarios/cross-project-denial.d.ts +0 -2
  52. package/dist/types/adapter-kit/parity-scenarios/envelope.d.ts +0 -2
  53. package/dist/types/adapter-kit/parity-scenarios/operation-context.d.ts +0 -3
  54. package/dist/types/adapter-kit/parity-scenarios/schema.d.ts +0 -2
  55. package/dist/types/adapter-kit/parity-scenarios/shared.d.ts +0 -32
  56. package/dist/types/adapter-kit/parity.d.ts +0 -43
  57. package/dist/types/security/crossProjectPolicy.d.ts +0 -110
  58. package/dist/types/security/operationContracts.d.ts +0 -84
  59. package/dist/types/security/resolveCrossProjectPolicy.d.ts +0 -39
  60. package/dist/types/utils/alias.d.ts +0 -7
@@ -1,22 +1,18 @@
1
+ import { pathToFileURL } from "node:url";
1
2
  import { ApiRouter } from "../router/index.mjs";
2
- import { buildOperationContractMap, createOperationSchemaHash, deriveOperationVersion } from "../security/operationContracts.mjs";
3
3
  import { Err, Ok } from "./result.mjs";
4
4
  import * as __rspack_external_path from "path";
5
- const getPackageInfo = (appDir)=>{
6
- try {
7
- const packageJsonPath = __rspack_external_path.resolve(appDir, './package.json');
8
- const packageJson = require(packageJsonPath);
9
- return {
10
- name: packageJson.name,
11
- version: packageJson.version
12
- };
13
- } catch (error) {
14
- return {};
5
+ class ClientCodegenError extends Error {
6
+ constructor(plugin, cause){
7
+ super(`BFF client code-generation plugin failed: ${plugin}`, {
8
+ cause
9
+ });
10
+ this.name = 'ClientCodegenError';
15
11
  }
16
- };
12
+ }
17
13
  const INNER_CLIENT_REQUEST_CREATOR = '@modern-js/plugin-bff/client';
18
- const generateClient = async ({ appDir, resourcePath, apiDir, lambdaDir, prefix, port, target, requestCreator, fetcher, requireResolve = require.resolve, httpMethodDecider, domain, requestId })=>{
19
- requestCreator = requestCreator || INNER_CLIENT_REQUEST_CREATOR;
14
+ const generateClient = async (options)=>{
15
+ const { appDir, resourcePath, apiDir, lambdaDir, prefix, port, target, fetcher, requireResolve = require.resolve, httpMethodDecider, domain, requestId, clientCodegenPlugin } = options;
20
16
  const apiRouter = new ApiRouter({
21
17
  appDir,
22
18
  apiDir,
@@ -26,131 +22,61 @@ const generateClient = async ({ appDir, resourcePath, apiDir, lambdaDir, prefix,
26
22
  });
27
23
  const handlerInfos = await apiRouter.getSingleModuleHandlers(resourcePath);
28
24
  if (!handlerInfos) return Err(`generate client error: Cannot require module ${resourcePath}`);
29
- const normalizedRequestId = requestId || 'default';
30
- const operationVersion = deriveOperationVersion(getPackageInfo(appDir).version);
31
- const operationContracts = buildOperationContractMap({
32
- handlers: handlerInfos,
33
- requestId: normalizedRequestId,
34
- operationVersion
35
- });
36
- const operationEntries = handlerInfos.map((handlerInfo)=>{
37
- const upperHttpMethod = handlerInfo.httpMethod.toUpperCase();
38
- const contract = operationContracts[`${upperHttpMethod}:${handlerInfo.routePath}`];
39
- return {
40
- name: handlerInfo.name,
41
- httpMethod: upperHttpMethod,
42
- routePath: handlerInfo.routePath,
43
- schemaHash: contract?.schemaHash ?? ''
44
- };
45
- }).sort((a, b)=>{
46
- const keyA = `${a.routePath}:${a.httpMethod}:${a.name}`;
47
- const keyB = `${b.routePath}:${b.httpMethod}:${b.name}`;
48
- return keyA.localeCompare(keyB);
49
- });
50
- const schemaHash = createOperationSchemaHash(operationEntries, normalizedRequestId);
51
- let hasUploadHandler = false;
52
- let handlersCode = '';
53
- for (const handlerInfo of handlerInfos){
54
- const { name, httpMethod, routePath, action } = handlerInfo;
55
- let exportStatement = `var ${name} =`;
56
- if ('default' === name.toLowerCase()) exportStatement = 'default';
57
- const upperHttpMethod = httpMethod.toUpperCase();
58
- const operationSchemaHash = operationContracts[`${upperHttpMethod}:${routePath}`]?.schemaHash ?? '';
59
- const operationContext = {
60
- operationId: name,
61
- routePath,
62
- method: upperHttpMethod,
63
- schemaHash: operationSchemaHash,
64
- operationVersion
65
- };
66
- if ('upload' === action) {
67
- hasUploadHandler = true;
68
- const uploadOptions = {
69
- path: routePath,
70
- ...domain ? {
71
- domain
72
- } : {},
73
- ...requestId ? {
74
- requestId
75
- } : {},
76
- ...requestId ? {
77
- operationContext
78
- } : {}
25
+ const draft = {
26
+ requestCreator: options.requestCreator || INNER_CLIENT_REQUEST_CREATOR,
27
+ imports: fetcher ? [
28
+ `import { fetch } from ${JSON.stringify(fetcher)};`
29
+ ] : [],
30
+ statements: [],
31
+ handlers: handlerInfos.map((handlerInfo)=>{
32
+ const optionProperties = [
33
+ `path: ${JSON.stringify(handlerInfo.routePath)}`
34
+ ];
35
+ if ('upload' !== handlerInfo.action) {
36
+ const portExpression = 'server' === target ? `process.env.PORT || ${String(port)}` : String(port);
37
+ optionProperties.push(`method: ${JSON.stringify(handlerInfo.httpMethod.toUpperCase())}`, `port: ${portExpression}`, `httpMethodDecider: ${JSON.stringify(httpMethodDecider || 'functionName')}`);
38
+ }
39
+ if (domain) optionProperties.push(`domain: ${JSON.stringify(domain)}`);
40
+ if (fetcher && 'upload' !== handlerInfo.action) optionProperties.push('fetch');
41
+ if (requestId) optionProperties.push(`requestId: ${JSON.stringify(requestId)}`);
42
+ return {
43
+ handlerInfo,
44
+ optionProperties
79
45
  };
80
- handlersCode += `export ${exportStatement} createUploader(${JSON.stringify(uploadOptions)});
81
- `;
82
- continue;
83
- }
84
- const portExpression = 'server' === target ? `process.env.PORT || ${String(port)}` : String(port);
85
- const requestOptionProperties = [
86
- `path: ${JSON.stringify(routePath)}`,
87
- `method: ${JSON.stringify(upperHttpMethod)}`,
88
- `port: ${portExpression}`,
89
- `httpMethodDecider: ${JSON.stringify(httpMethodDecider ? httpMethodDecider : 'functionName')}`
90
- ];
91
- if (domain) requestOptionProperties.push(`domain: ${JSON.stringify(domain)}`);
92
- if (fetcher) requestOptionProperties.push('fetch');
93
- if (requestId) requestOptionProperties.push(`requestId: ${JSON.stringify(requestId)}`);
94
- requestOptionProperties.push(`operationContext: ${JSON.stringify(operationContext)}`);
95
- handlersCode += `export ${exportStatement} createRequest({ ${requestOptionProperties.join(', ')} });
96
- `;
46
+ })
47
+ };
48
+ if (clientCodegenPlugin) try {
49
+ const specifier = clientCodegenPlugin.startsWith('.') ? __rspack_external_path.resolve(appDir, clientCodegenPlugin) : clientCodegenPlugin;
50
+ const resolved = requireResolve(specifier, {
51
+ paths: [
52
+ appDir
53
+ ]
54
+ });
55
+ const extension = await import(pathToFileURL(resolved).href);
56
+ const modifyClient = extension.modifyClient ?? extension.default?.modifyClient;
57
+ if ('function' != typeof modifyClient) throw new TypeError('The module must export a modifyClient function.');
58
+ await modifyClient(draft, {
59
+ options,
60
+ handlerInfos
61
+ });
62
+ } catch (cause) {
63
+ throw new ClientCodegenError(clientCodegenPlugin, cause);
97
64
  }
98
- const serializedRequestCreator = JSON.stringify(requestCreator);
99
- const serializedFetcher = fetcher ? JSON.stringify(fetcher) : void 0;
100
- const namedRequestImports = `createRequest${hasUploadHandler ? ', createUploader' : ''}`;
101
- const importCode = requestId ? `import * as requestRuntime from ${serializedRequestCreator};
102
- const { ${namedRequestImports} } = requestRuntime;
103
- ${serializedFetcher ? `import { fetch } from ${serializedFetcher};\n` : ''}` : `import { ${namedRequestImports} } from ${serializedRequestCreator};
104
- ${serializedFetcher ? `import { fetch } from ${serializedFetcher};\n` : ''}`;
105
- const bootstrapCode = requestId ? `export const initProducerClient = (options = {}) => {
106
- const configure = requestRuntime.configure;
107
- if (typeof configure !== 'function') {
108
- console.warn('[modernjs] Compatibility request creator path does not expose configure(); use default @modern-js/create-request or migrate the compatibility path.');
109
- return undefined;
110
- }
111
- const defaultSecureOptions = {
112
- requestId: ${JSON.stringify(requestId)},
113
- requireEnvelope: true,
114
- identityBinding: {
115
- enabled: true,
116
- strict: true,
117
- },
118
- operationContract: {
119
- enabled: true,
120
- strict: true,
121
- requireSchemaHash: true,
122
- requireOperationVersion: true,
123
- },
124
- };
125
- return configure({
126
- ...defaultSecureOptions,
127
- ...options,
128
- identityBinding: {
129
- ...defaultSecureOptions.identityBinding,
130
- ...(options && options.identityBinding ? options.identityBinding : {}),
131
- },
132
- operationContract: {
133
- ...defaultSecureOptions.operationContract,
134
- ...(options && options.operationContract ? options.operationContract : {}),
135
- },
136
- });
137
- };
138
- ` : '';
139
- const manifestCode = `export const operationVersion = ${String(operationVersion)};
140
- export const operationSchemaHash = '${schemaHash}';
141
- export const operationManifest = ${JSON.stringify({
142
- operationVersion,
143
- schemaHash,
144
- operations: operationEntries
145
- }, null, 2)};
146
- `;
147
- const generatedParts = [
148
- importCode.trimEnd(),
149
- bootstrapCode.trimEnd(),
150
- manifestCode.trimEnd(),
151
- handlersCode.trimEnd()
152
- ].filter(Boolean);
153
- return Ok(`${generatedParts.join('\n\n')}
154
- `);
65
+ const hasUploadHandler = handlerInfos.some((info)=>'upload' === info.action);
66
+ const requestImports = `createRequest${hasUploadHandler ? ', createUploader' : ''}`;
67
+ const importCode = [
68
+ `import { ${requestImports} } from ${JSON.stringify(draft.requestCreator)};`,
69
+ ...draft.imports
70
+ ].join('\n');
71
+ const handlersCode = draft.handlers.map(({ handlerInfo, optionProperties })=>{
72
+ const exportStatement = 'default' === handlerInfo.name.toLowerCase() ? 'default' : `var ${handlerInfo.name} =`;
73
+ const creator = 'upload' === handlerInfo.action ? 'createUploader' : 'createRequest';
74
+ return `export ${exportStatement} ${creator}({ ${optionProperties.join(', ')} });`;
75
+ }).join('\n');
76
+ return Ok(`${[
77
+ importCode,
78
+ ...draft.statements,
79
+ handlersCode
80
+ ].filter(Boolean).join('\n\n')}\n`);
155
81
  };
156
- export { INNER_CLIENT_REQUEST_CREATOR, generateClient };
82
+ export { ClientCodegenError, INNER_CLIENT_REQUEST_CREATOR, generateClient };
@@ -1,11 +1,7 @@
1
- export * from "./adapter-kit/index.mjs";
2
1
  export * from "./client/index.mjs";
3
2
  export * from "./operators/http.mjs";
4
3
  export * from "./router/index.mjs";
5
- export * from "./security/crossProjectPolicy.mjs";
6
- export * from "./security/operationContracts.mjs";
7
- export * from "./security/resolveCrossProjectPolicy.mjs";
8
4
  export * from "./types.mjs";
9
5
  export { Api } from "./api.mjs";
10
6
  export { HttpError, ValidationError } from "./errors/http.mjs";
11
- export { HANDLER_WITH_META, INPUT_PARAMS_DECIDER, createStorage, getRelativeRuntimePath, isInputParamsDeciderHandler, isWithMetaHandler, registerPaths } from "./utils/index.mjs";
7
+ export { HANDLER_WITH_META, INPUT_PARAMS_DECIDER, createStorage, isInputParamsDeciderHandler, isWithMetaHandler } from "./utils/index.mjs";
@@ -88,10 +88,11 @@ class ApiRouter {
88
88
  if ('test' !== process.env.NODE_ENV) logger.warn(`Only api handlers are allowd to be exported, please remove the function ${originHandlerName} from exports`);
89
89
  return null;
90
90
  }
91
+ } else {
92
+ if (!handler) return null;
93
+ if ('function' == typeof handler && handler.length > 0) return HttpMethod.Post;
94
+ return HttpMethod.Get;
91
95
  }
92
- if (!handler) return null;
93
- if ('function' == typeof handler && handler.length > 0) return HttpMethod.Post;
94
- return HttpMethod.Get;
95
96
  }
96
97
  getAction(handler) {
97
98
  if (handler) {
@@ -140,9 +141,10 @@ class ApiRouter {
140
141
  cause: err
141
142
  });
142
143
  throw error;
144
+ } else {
145
+ console.error(`Failed to load BFF module '${filename}':`, err);
146
+ return null;
143
147
  }
144
- console.error(`Failed to load BFF module '${filename}':`, err);
145
- return null;
146
148
  }
147
149
  }
148
150
  getHandlerInfos(moduleInfos) {
@@ -1,4 +1,3 @@
1
- export * from "./alias.mjs";
2
1
  export * from "./meta.mjs";
3
2
  export * from "./storage.mjs";
4
3
  export * from "./validate.mjs";
@@ -1,24 +1,20 @@
1
1
  import __rslib_shim_module__ from "node:module";
2
2
  const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
3
+ import { pathToFileURL } from "node:url";
3
4
  import { ApiRouter } from "../router/index.mjs";
4
- import { buildOperationContractMap, createOperationSchemaHash, deriveOperationVersion } from "../security/operationContracts.mjs";
5
5
  import { Err, Ok } from "./result.mjs";
6
6
  import * as __rspack_external_path from "path";
7
- const getPackageInfo = (appDir)=>{
8
- try {
9
- const packageJsonPath = __rspack_external_path.resolve(appDir, './package.json');
10
- const packageJson = require(packageJsonPath);
11
- return {
12
- name: packageJson.name,
13
- version: packageJson.version
14
- };
15
- } catch (error) {
16
- return {};
7
+ class ClientCodegenError extends Error {
8
+ constructor(plugin, cause){
9
+ super(`BFF client code-generation plugin failed: ${plugin}`, {
10
+ cause
11
+ });
12
+ this.name = 'ClientCodegenError';
17
13
  }
18
- };
14
+ }
19
15
  const INNER_CLIENT_REQUEST_CREATOR = '@modern-js/plugin-bff/client';
20
- const generateClient = async ({ appDir, resourcePath, apiDir, lambdaDir, prefix, port, target, requestCreator, fetcher, requireResolve = require.resolve, httpMethodDecider, domain, requestId })=>{
21
- requestCreator = requestCreator || INNER_CLIENT_REQUEST_CREATOR;
16
+ const generateClient = async (options)=>{
17
+ const { appDir, resourcePath, apiDir, lambdaDir, prefix, port, target, fetcher, requireResolve = require.resolve, httpMethodDecider, domain, requestId, clientCodegenPlugin } = options;
22
18
  const apiRouter = new ApiRouter({
23
19
  appDir,
24
20
  apiDir,
@@ -28,131 +24,61 @@ const generateClient = async ({ appDir, resourcePath, apiDir, lambdaDir, prefix,
28
24
  });
29
25
  const handlerInfos = await apiRouter.getSingleModuleHandlers(resourcePath);
30
26
  if (!handlerInfos) return Err(`generate client error: Cannot require module ${resourcePath}`);
31
- const normalizedRequestId = requestId || 'default';
32
- const operationVersion = deriveOperationVersion(getPackageInfo(appDir).version);
33
- const operationContracts = buildOperationContractMap({
34
- handlers: handlerInfos,
35
- requestId: normalizedRequestId,
36
- operationVersion
37
- });
38
- const operationEntries = handlerInfos.map((handlerInfo)=>{
39
- const upperHttpMethod = handlerInfo.httpMethod.toUpperCase();
40
- const contract = operationContracts[`${upperHttpMethod}:${handlerInfo.routePath}`];
41
- return {
42
- name: handlerInfo.name,
43
- httpMethod: upperHttpMethod,
44
- routePath: handlerInfo.routePath,
45
- schemaHash: contract?.schemaHash ?? ''
46
- };
47
- }).sort((a, b)=>{
48
- const keyA = `${a.routePath}:${a.httpMethod}:${a.name}`;
49
- const keyB = `${b.routePath}:${b.httpMethod}:${b.name}`;
50
- return keyA.localeCompare(keyB);
51
- });
52
- const schemaHash = createOperationSchemaHash(operationEntries, normalizedRequestId);
53
- let hasUploadHandler = false;
54
- let handlersCode = '';
55
- for (const handlerInfo of handlerInfos){
56
- const { name, httpMethod, routePath, action } = handlerInfo;
57
- let exportStatement = `var ${name} =`;
58
- if ('default' === name.toLowerCase()) exportStatement = 'default';
59
- const upperHttpMethod = httpMethod.toUpperCase();
60
- const operationSchemaHash = operationContracts[`${upperHttpMethod}:${routePath}`]?.schemaHash ?? '';
61
- const operationContext = {
62
- operationId: name,
63
- routePath,
64
- method: upperHttpMethod,
65
- schemaHash: operationSchemaHash,
66
- operationVersion
67
- };
68
- if ('upload' === action) {
69
- hasUploadHandler = true;
70
- const uploadOptions = {
71
- path: routePath,
72
- ...domain ? {
73
- domain
74
- } : {},
75
- ...requestId ? {
76
- requestId
77
- } : {},
78
- ...requestId ? {
79
- operationContext
80
- } : {}
27
+ const draft = {
28
+ requestCreator: options.requestCreator || INNER_CLIENT_REQUEST_CREATOR,
29
+ imports: fetcher ? [
30
+ `import { fetch } from ${JSON.stringify(fetcher)};`
31
+ ] : [],
32
+ statements: [],
33
+ handlers: handlerInfos.map((handlerInfo)=>{
34
+ const optionProperties = [
35
+ `path: ${JSON.stringify(handlerInfo.routePath)}`
36
+ ];
37
+ if ('upload' !== handlerInfo.action) {
38
+ const portExpression = 'server' === target ? `process.env.PORT || ${String(port)}` : String(port);
39
+ optionProperties.push(`method: ${JSON.stringify(handlerInfo.httpMethod.toUpperCase())}`, `port: ${portExpression}`, `httpMethodDecider: ${JSON.stringify(httpMethodDecider || 'functionName')}`);
40
+ }
41
+ if (domain) optionProperties.push(`domain: ${JSON.stringify(domain)}`);
42
+ if (fetcher && 'upload' !== handlerInfo.action) optionProperties.push('fetch');
43
+ if (requestId) optionProperties.push(`requestId: ${JSON.stringify(requestId)}`);
44
+ return {
45
+ handlerInfo,
46
+ optionProperties
81
47
  };
82
- handlersCode += `export ${exportStatement} createUploader(${JSON.stringify(uploadOptions)});
83
- `;
84
- continue;
85
- }
86
- const portExpression = 'server' === target ? `process.env.PORT || ${String(port)}` : String(port);
87
- const requestOptionProperties = [
88
- `path: ${JSON.stringify(routePath)}`,
89
- `method: ${JSON.stringify(upperHttpMethod)}`,
90
- `port: ${portExpression}`,
91
- `httpMethodDecider: ${JSON.stringify(httpMethodDecider ? httpMethodDecider : 'functionName')}`
92
- ];
93
- if (domain) requestOptionProperties.push(`domain: ${JSON.stringify(domain)}`);
94
- if (fetcher) requestOptionProperties.push('fetch');
95
- if (requestId) requestOptionProperties.push(`requestId: ${JSON.stringify(requestId)}`);
96
- requestOptionProperties.push(`operationContext: ${JSON.stringify(operationContext)}`);
97
- handlersCode += `export ${exportStatement} createRequest({ ${requestOptionProperties.join(', ')} });
98
- `;
48
+ })
49
+ };
50
+ if (clientCodegenPlugin) try {
51
+ const specifier = clientCodegenPlugin.startsWith('.') ? __rspack_external_path.resolve(appDir, clientCodegenPlugin) : clientCodegenPlugin;
52
+ const resolved = requireResolve(specifier, {
53
+ paths: [
54
+ appDir
55
+ ]
56
+ });
57
+ const extension = await import(pathToFileURL(resolved).href);
58
+ const modifyClient = extension.modifyClient ?? extension.default?.modifyClient;
59
+ if ('function' != typeof modifyClient) throw new TypeError('The module must export a modifyClient function.');
60
+ await modifyClient(draft, {
61
+ options,
62
+ handlerInfos
63
+ });
64
+ } catch (cause) {
65
+ throw new ClientCodegenError(clientCodegenPlugin, cause);
99
66
  }
100
- const serializedRequestCreator = JSON.stringify(requestCreator);
101
- const serializedFetcher = fetcher ? JSON.stringify(fetcher) : void 0;
102
- const namedRequestImports = `createRequest${hasUploadHandler ? ', createUploader' : ''}`;
103
- const importCode = requestId ? `import * as requestRuntime from ${serializedRequestCreator};
104
- const { ${namedRequestImports} } = requestRuntime;
105
- ${serializedFetcher ? `import { fetch } from ${serializedFetcher};\n` : ''}` : `import { ${namedRequestImports} } from ${serializedRequestCreator};
106
- ${serializedFetcher ? `import { fetch } from ${serializedFetcher};\n` : ''}`;
107
- const bootstrapCode = requestId ? `export const initProducerClient = (options = {}) => {
108
- const configure = requestRuntime.configure;
109
- if (typeof configure !== 'function') {
110
- console.warn('[modernjs] Compatibility request creator path does not expose configure(); use default @modern-js/create-request or migrate the compatibility path.');
111
- return undefined;
112
- }
113
- const defaultSecureOptions = {
114
- requestId: ${JSON.stringify(requestId)},
115
- requireEnvelope: true,
116
- identityBinding: {
117
- enabled: true,
118
- strict: true,
119
- },
120
- operationContract: {
121
- enabled: true,
122
- strict: true,
123
- requireSchemaHash: true,
124
- requireOperationVersion: true,
125
- },
126
- };
127
- return configure({
128
- ...defaultSecureOptions,
129
- ...options,
130
- identityBinding: {
131
- ...defaultSecureOptions.identityBinding,
132
- ...(options && options.identityBinding ? options.identityBinding : {}),
133
- },
134
- operationContract: {
135
- ...defaultSecureOptions.operationContract,
136
- ...(options && options.operationContract ? options.operationContract : {}),
137
- },
138
- });
139
- };
140
- ` : '';
141
- const manifestCode = `export const operationVersion = ${String(operationVersion)};
142
- export const operationSchemaHash = '${schemaHash}';
143
- export const operationManifest = ${JSON.stringify({
144
- operationVersion,
145
- schemaHash,
146
- operations: operationEntries
147
- }, null, 2)};
148
- `;
149
- const generatedParts = [
150
- importCode.trimEnd(),
151
- bootstrapCode.trimEnd(),
152
- manifestCode.trimEnd(),
153
- handlersCode.trimEnd()
154
- ].filter(Boolean);
155
- return Ok(`${generatedParts.join('\n\n')}
156
- `);
67
+ const hasUploadHandler = handlerInfos.some((info)=>'upload' === info.action);
68
+ const requestImports = `createRequest${hasUploadHandler ? ', createUploader' : ''}`;
69
+ const importCode = [
70
+ `import { ${requestImports} } from ${JSON.stringify(draft.requestCreator)};`,
71
+ ...draft.imports
72
+ ].join('\n');
73
+ const handlersCode = draft.handlers.map(({ handlerInfo, optionProperties })=>{
74
+ const exportStatement = 'default' === handlerInfo.name.toLowerCase() ? 'default' : `var ${handlerInfo.name} =`;
75
+ const creator = 'upload' === handlerInfo.action ? 'createUploader' : 'createRequest';
76
+ return `export ${exportStatement} ${creator}({ ${optionProperties.join(', ')} });`;
77
+ }).join('\n');
78
+ return Ok(`${[
79
+ importCode,
80
+ ...draft.statements,
81
+ handlersCode
82
+ ].filter(Boolean).join('\n\n')}\n`);
157
83
  };
158
- export { INNER_CLIENT_REQUEST_CREATOR, generateClient };
84
+ export { ClientCodegenError, INNER_CLIENT_REQUEST_CREATOR, generateClient };
@@ -1,12 +1,8 @@
1
1
  import "node:module";
2
- export * from "./adapter-kit/index.mjs";
3
2
  export * from "./client/index.mjs";
4
3
  export * from "./operators/http.mjs";
5
4
  export * from "./router/index.mjs";
6
- export * from "./security/crossProjectPolicy.mjs";
7
- export * from "./security/operationContracts.mjs";
8
- export * from "./security/resolveCrossProjectPolicy.mjs";
9
5
  export * from "./types.mjs";
10
6
  export { Api } from "./api.mjs";
11
7
  export { HttpError, ValidationError } from "./errors/http.mjs";
12
- export { HANDLER_WITH_META, INPUT_PARAMS_DECIDER, createStorage, getRelativeRuntimePath, isInputParamsDeciderHandler, isWithMetaHandler, registerPaths } from "./utils/index.mjs";
8
+ export { HANDLER_WITH_META, INPUT_PARAMS_DECIDER, createStorage, isInputParamsDeciderHandler, isWithMetaHandler } from "./utils/index.mjs";
@@ -89,10 +89,11 @@ class ApiRouter {
89
89
  if ('test' !== process.env.NODE_ENV) logger.warn(`Only api handlers are allowd to be exported, please remove the function ${originHandlerName} from exports`);
90
90
  return null;
91
91
  }
92
+ } else {
93
+ if (!handler) return null;
94
+ if ('function' == typeof handler && handler.length > 0) return HttpMethod.Post;
95
+ return HttpMethod.Get;
92
96
  }
93
- if (!handler) return null;
94
- if ('function' == typeof handler && handler.length > 0) return HttpMethod.Post;
95
- return HttpMethod.Get;
96
97
  }
97
98
  getAction(handler) {
98
99
  if (handler) {
@@ -141,9 +142,10 @@ class ApiRouter {
141
142
  cause: err
142
143
  });
143
144
  throw error;
145
+ } else {
146
+ console.error(`Failed to load BFF module '${filename}':`, err);
147
+ return null;
144
148
  }
145
- console.error(`Failed to load BFF module '${filename}':`, err);
146
- return null;
147
149
  }
148
150
  }
149
151
  getHandlerInfos(moduleInfos) {
@@ -1,5 +1,4 @@
1
1
  import "node:module";
2
- export * from "./alias.mjs";
3
2
  export * from "./meta.mjs";
4
3
  export * from "./storage.mjs";
5
4
  export * from "./validate.mjs";
@@ -1,4 +1,5 @@
1
1
  import type { HttpMethodDecider } from '@modern-js/types';
2
+ import { type APIHandlerInfo } from '../router/index.js';
2
3
  import { type Result } from './result.js';
3
4
  export type GenClientResult = Result<string>;
4
5
  export type GenClientOptions = {
@@ -16,6 +17,28 @@ export type GenClientOptions = {
16
17
  httpMethodDecider?: HttpMethodDecider;
17
18
  domain?: string;
18
19
  requestId?: string;
20
+ /** Node module exporting a modifyClient code-generation transform. */
21
+ clientCodegenPlugin?: string;
19
22
  };
23
+ export interface ClientHandlerDraft {
24
+ readonly handlerInfo: APIHandlerInfo;
25
+ /** Source expressions, including native server port and fetch bindings. */
26
+ optionProperties: string[];
27
+ }
28
+ export interface ClientModuleDraft {
29
+ requestCreator: string;
30
+ imports: string[];
31
+ statements: string[];
32
+ readonly handlers: readonly ClientHandlerDraft[];
33
+ }
34
+ export interface ClientCodegenContext {
35
+ readonly options: Readonly<GenClientOptions>;
36
+ readonly handlerInfos: readonly APIHandlerInfo[];
37
+ }
38
+ export type ClientCodegenPlugin = (draft: ClientModuleDraft, context: ClientCodegenContext) => void | Promise<void>;
39
+ /** A failed transform must not be downgraded to best-effort publication. */
40
+ export declare class ClientCodegenError extends Error {
41
+ constructor(plugin: string, cause: unknown);
42
+ }
20
43
  export declare const INNER_CLIENT_REQUEST_CREATOR = "@modern-js/plugin-bff/client";
21
- export declare const generateClient: ({ appDir, resourcePath, apiDir, lambdaDir, prefix, port, target, requestCreator, fetcher, requireResolve, httpMethodDecider, domain, requestId, }: GenClientOptions) => Promise<GenClientResult>;
44
+ export declare const generateClient: (options: GenClientOptions) => Promise<GenClientResult>;
@@ -1,12 +1,8 @@
1
- export * from './adapter-kit/index.js';
2
1
  export { Api } from './api.js';
3
2
  export * from './client/index.js';
4
3
  export type * from './compatible.js';
5
4
  export { HttpError, ValidationError } from './errors/http.js';
6
5
  export * from './operators/http.js';
7
6
  export * from './router/index.js';
8
- export * from './security/crossProjectPolicy.js';
9
- export * from './security/operationContracts.js';
10
- export * from './security/resolveCrossProjectPolicy.js';
11
7
  export * from './types.js';
12
- export { createStorage, getRelativeRuntimePath, HANDLER_WITH_META, INPUT_PARAMS_DECIDER, isInputParamsDeciderHandler, isWithMetaHandler, registerPaths, } from './utils/index.js';
8
+ export { createStorage, HANDLER_WITH_META, INPUT_PARAMS_DECIDER, isInputParamsDeciderHandler, isWithMetaHandler, } from './utils/index.js';
@@ -1,4 +1,3 @@
1
- export * from './alias.js';
2
1
  export { debug } from './debug.js';
3
2
  export * from './meta.js';
4
3
  export * from './storage.js';