@bleedingdev/modern-js-plugin-bff 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6

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 (78) hide show
  1. package/dist/cjs/cli/generator.js +118 -117
  2. package/dist/cjs/cli.js +11 -6
  3. package/dist/cjs/loader.js +9 -65
  4. package/dist/cjs/runtime/create-request/index.js +0 -6
  5. package/dist/cjs/runtime/hono/adapter.js +8 -12
  6. package/dist/cjs/server.js +26 -18
  7. package/dist/cjs/utils/client-generator/files.js +1 -1
  8. package/dist/cjs/utils/client-generator/generate.js +46 -45
  9. package/dist/cjs/utils/client-generator/package-json.js +2 -2
  10. package/dist/cjs/utils/client-generator/write-package.js +18 -1
  11. package/dist/cjs/utils/crossProjectApiPlugin.js +26 -83
  12. package/dist/cjs/utils/pluginGenerator.js +26 -26
  13. package/dist/cjs/utils/runtimeGenerator.js +26 -75
  14. package/dist/esm/cli/generator.mjs +121 -120
  15. package/dist/esm/cli.mjs +11 -6
  16. package/dist/esm/loader.mjs +9 -55
  17. package/dist/esm/runtime/create-request/index.mjs +1 -1
  18. package/dist/esm/runtime/hono/adapter.mjs +8 -12
  19. package/dist/esm/server.mjs +27 -19
  20. package/dist/esm/utils/client-generator/files.mjs +1 -1
  21. package/dist/esm/utils/client-generator/generate.mjs +47 -46
  22. package/dist/esm/utils/client-generator/package-json.mjs +3 -3
  23. package/dist/esm/utils/client-generator/write-package.mjs +18 -1
  24. package/dist/esm/utils/crossProjectApiPlugin.mjs +24 -54
  25. package/dist/esm/utils/pluginGenerator.mjs +23 -25
  26. package/dist/esm/utils/runtimeGenerator.mjs +23 -74
  27. package/dist/esm-node/cli/generator.mjs +121 -120
  28. package/dist/esm-node/cli.mjs +11 -6
  29. package/dist/esm-node/loader.mjs +9 -55
  30. package/dist/esm-node/runtime/create-request/index.mjs +1 -1
  31. package/dist/esm-node/runtime/hono/adapter.mjs +8 -12
  32. package/dist/esm-node/server.mjs +27 -19
  33. package/dist/esm-node/utils/client-generator/files.mjs +1 -1
  34. package/dist/esm-node/utils/client-generator/generate.mjs +47 -46
  35. package/dist/esm-node/utils/client-generator/package-json.mjs +3 -3
  36. package/dist/esm-node/utils/client-generator/write-package.mjs +18 -1
  37. package/dist/esm-node/utils/crossProjectApiPlugin.mjs +24 -54
  38. package/dist/esm-node/utils/pluginGenerator.mjs +23 -28
  39. package/dist/esm-node/utils/runtimeGenerator.mjs +24 -76
  40. package/dist/types/cli/generator.d.ts +2 -2
  41. package/dist/types/loader.d.ts +1 -11
  42. package/dist/types/runtime/create-request/index.d.ts +1 -3
  43. package/dist/types/runtime/hono/adapter.d.ts +9 -2
  44. package/dist/types/server.d.ts +9 -2
  45. package/dist/types/utils/client-generator/generate.d.ts +8 -12
  46. package/dist/types/utils/client-generator/write-package.d.ts +1 -1
  47. package/dist/types/utils/crossProjectApiPlugin.d.ts +18 -12
  48. package/dist/types/utils/pluginGenerator.d.ts +6 -7
  49. package/dist/types/utils/runtimeGenerator.d.ts +6 -3
  50. package/package.json +25 -118
  51. package/dist/cjs/cli/serverGlobalVars.js +0 -113
  52. package/dist/cjs/runtime/data-platform/index.js +0 -62
  53. package/dist/cjs/runtime/effect/edge-dispatcher.js +0 -62
  54. package/dist/cjs/runtime/effect/edge.js +0 -69
  55. package/dist/cjs/runtime/effect/index.js +0 -305
  56. package/dist/cjs/runtime/effect-client/index.js +0 -62
  57. package/dist/cjs/runtime/effect-client/runtime.js +0 -62
  58. package/dist/esm/cli/serverGlobalVars.mjs +0 -72
  59. package/dist/esm/runtime/data-platform/index.mjs +0 -1
  60. package/dist/esm/runtime/effect/edge-dispatcher.mjs +0 -1
  61. package/dist/esm/runtime/effect/edge.mjs +0 -2
  62. package/dist/esm/runtime/effect/index.mjs +0 -16
  63. package/dist/esm/runtime/effect-client/index.mjs +0 -1
  64. package/dist/esm/runtime/effect-client/runtime.mjs +0 -1
  65. package/dist/esm-node/cli/serverGlobalVars.mjs +0 -73
  66. package/dist/esm-node/runtime/data-platform/index.mjs +0 -2
  67. package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +0 -2
  68. package/dist/esm-node/runtime/effect/edge.mjs +0 -3
  69. package/dist/esm-node/runtime/effect/index.mjs +0 -17
  70. package/dist/esm-node/runtime/effect-client/index.mjs +0 -2
  71. package/dist/esm-node/runtime/effect-client/runtime.mjs +0 -2
  72. package/dist/types/cli/serverGlobalVars.d.ts +0 -6
  73. package/dist/types/runtime/data-platform/index.d.ts +0 -1
  74. package/dist/types/runtime/effect/edge-dispatcher.d.ts +0 -1
  75. package/dist/types/runtime/effect/edge.d.ts +0 -2
  76. package/dist/types/runtime/effect/index.d.ts +0 -15
  77. package/dist/types/runtime/effect-client/index.d.ts +0 -1
  78. package/dist/types/runtime/effect-client/runtime.d.ts +0 -1
@@ -1,133 +1,134 @@
1
1
  import "node:module";
2
- import { ApiRouter, buildOperationContractMap, deriveOperationVersion } from "@modern-js/bff-core";
2
+ import { ApiRouter } from "@modern-js/bff-core";
3
3
  import { compile } from "@modern-js/server-utils";
4
4
  import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig, upath } from "@modern-js/utils";
5
5
  import clientGenerator from "../utils/clientGenerator.mjs";
6
- import pluginGenerator from "../utils/pluginGenerator.mjs";
7
- import runtimeGenerator from "../utils/runtimeGenerator.mjs";
6
+ import pluginGenerator, { renderBffPlugin } from "../utils/pluginGenerator.mjs";
7
+ import runtimeGenerator, { renderBffRuntime } from "../utils/runtimeGenerator.mjs";
8
8
  import { getPrimaryPrefix } from "./prefix.mjs";
9
- import { serializeServerGlobalVars, transformServerGlobalVars } from "./serverGlobalVars.mjs";
10
9
  const RUNTIME_CREATE_REQUEST = '@modern-js/plugin-bff/client';
11
10
  const createBffGenerator = (api)=>{
12
- const compileApi = ()=>Promise.resolve().then(()=>{
13
- const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
14
- const modernConfig = api.getNormalizedConfig();
15
- const distDir = upath.resolve(distDirectory);
16
- const apiDir = apiDirectory || upath.resolve(appDirectory, API_DIR);
17
- const sharedDir = sharedDirectory || upath.resolve(appDirectory, SHARED_DIR);
18
- const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
19
- const sourceDirs = [];
20
- return fs.pathExists(apiDir).then((apiDirectoryExists)=>{
21
- if (apiDirectoryExists) sourceDirs.push(apiDir);
22
- return fs.pathExists(sharedDir);
23
- }).then((sharedDirectoryExists)=>{
24
- if (sharedDirectoryExists) sourceDirs.push(sharedDir);
25
- if (0 === sourceDirs.length) return;
26
- const { alias, globalVars } = modernConfig.source;
27
- const { alias: resolveAlias } = modernConfig.resolve;
28
- const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
29
- const serializedGlobalVars = serializeServerGlobalVars(globalVars);
30
- return compile(appDirectory, {
31
- alias: combinedAlias
32
- }, {
33
- sourceDirs,
34
- distDir,
35
- tsconfigPath,
36
- moduleType,
37
- throwErrorInsteadOfExit: true
38
- }).then(()=>transformServerGlobalVars(sourceDirs.map((sourceDir)=>upath.resolve(distDir, upath.relative(appDirectory, sourceDir))), serializedGlobalVars)).then(async ()=>{
39
- if ('effect' !== bffRuntimeFramework) return;
40
- const { bundleBuiltEffectEntryForNode } = await import("@modern-js/plugin-bff-extensions/client-generator");
41
- return bundleBuiltEffectEntryForNode({
42
- appDir: appDirectory,
43
- apiDir,
44
- distDir,
45
- effectEntry: modernConfig.bff?.effect?.entry,
46
- format: 'module' === moduleType ? 'esm' : 'cjs'
47
- });
48
- });
49
- });
11
+ const compileApi = async ()=>{
12
+ const { appDirectory, distDirectory, apiDirectory, sharedDirectory, moduleType } = api.getAppContext();
13
+ const config = api.getNormalizedConfig();
14
+ const distDir = upath.resolve(distDirectory);
15
+ const apiDir = apiDirectory || upath.resolve(appDirectory, API_DIR);
16
+ const sharedDir = sharedDirectory || upath.resolve(appDirectory, SHARED_DIR);
17
+ const tsconfigPath = resolveServerTsconfig(appDirectory, config.server?.tsconfigPath);
18
+ const sourceDirs = [];
19
+ for (const directory of [
20
+ apiDir,
21
+ sharedDir
22
+ ])if (await fs.pathExists(directory)) sourceDirs.push(directory);
23
+ if (0 === sourceDirs.length) return;
24
+ const combinedAlias = [].concat(config.source.alias ?? []).concat(config.resolve.alias ?? []);
25
+ const context = {
26
+ appDirectory,
27
+ apiDirectory: apiDir,
28
+ sourceDirectories: sourceDirs,
29
+ outputDirectories: sourceDirs.map((directory)=>upath.resolve(distDir, upath.relative(appDirectory, directory))),
30
+ distDirectory: distDir,
31
+ tsconfigPath,
32
+ moduleType
33
+ };
34
+ const hooks = api.getHooks();
35
+ await hooks.onBeforeBffCompile.call(context);
36
+ await compile(appDirectory, {
37
+ alias: combinedAlias
38
+ }, {
39
+ sourceDirs,
40
+ distDir,
41
+ tsconfigPath,
42
+ moduleType,
43
+ excludeFiles: api.getAppContext().serverCompileExcludedFiles,
44
+ throwErrorInsteadOfExit: true
50
45
  });
51
- const generate = ()=>Promise.resolve().then(async ()=>{
52
- const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework, packageName } = api.getAppContext();
53
- const modernConfig = api.getNormalizedConfig();
54
- const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
55
- const { bff } = modernConfig || {};
56
- const prefix = getPrimaryPrefix(bff?.prefix);
57
- const httpMethodDecider = bff?.httpMethodDecider;
58
- const apiRouter = new ApiRouter({
59
- apiDir: apiDirectory,
60
- appDir: appDirectory,
61
- lambdaDir: lambdaDirectory,
62
- prefix,
63
- httpMethodDecider,
64
- isBuild: true
65
- });
66
- const lambdaDir = apiRouter.getLambdaDir();
67
- const existLambda = apiRouter.isExistLambda();
68
- const packageJson = fs.readJSONSync(upath.resolve(appDirectory, 'package.json'));
69
- const requestId = bff?.requestId || packageJson.name || packageName || 'default';
70
- const operationContractsPromise = 'effect' !== bffRuntimeFramework && existLambda ? apiRouter.getApiHandlers().then((handlers)=>buildOperationContractMap({
71
- handlers,
72
- requestId,
73
- operationVersion: deriveOperationVersion(packageJson.version)
74
- })) : Promise.resolve({});
75
- const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
76
- const relativeApiPath = upath.relative(appDirectory, apiDirectory);
77
- const relativeLambdaPath = upath.relative(appDirectory, lambdaDir);
78
- const { sourceEffectEntry, relativeEffectEntry } = 'effect' === bffRuntimeFramework ? (await import("@modern-js/plugin-bff-extensions/client-generator")).resolveEffectEntryPaths({
79
- appDir: appDirectory,
80
- apiDir: apiDirectory,
81
- effectEntry: bff?.effect?.entry
82
- }) : {
83
- sourceEffectEntry: void 0,
84
- relativeEffectEntry: ''
85
- };
86
- const clientGenerationPromise = clientGenerator({
87
- prefix,
88
- appDir: appDirectory,
89
- apiDir: apiDirectory,
90
- lambdaDir,
91
- existLambda,
92
- port,
93
- requestId,
94
- requestCreator: bff?.requestCreator,
95
- httpMethodDecider,
96
- relativeDistPath,
97
- relativeApiPath,
98
- apiFiles: apiRouter.getApiFiles(),
99
- bffRuntimeFramework,
100
- effectEntry: bff?.effect?.entry,
101
- effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
102
- });
103
- return Promise.all([
104
- operationContractsPromise,
105
- clientGenerationPromise
106
- ]).then(([lambdaContracts, generatedEffectClient])=>{
107
- if ('effect' === bffRuntimeFramework && null === generatedEffectClient) throw new Error(`Effect cross-project client generation failed for ${sourceEffectEntry ?? apiDirectory}.`);
108
- const operationContracts = 'effect' === bffRuntimeFramework ? generatedEffectClient?.operationContracts ?? {} : lambdaContracts;
109
- return pluginGenerator({
110
- prefix,
111
- appDirectory,
112
- requestId,
113
- relativeDistPath,
114
- relativeApiPath,
115
- relativeLambdaPath,
116
- runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect',
117
- relativeEffectEntry,
118
- operationContracts
119
- }).then(()=>runtimeGenerator({
120
- runtime,
121
- appDirectory,
122
- relativeDistPath
123
- }));
124
- });
46
+ await hooks.onAfterBffCompile.call(context);
47
+ };
48
+ const generate = async ()=>{
49
+ const { appDirectory, apiDirectory, lambdaDirectory, port, packageName } = api.getAppContext();
50
+ const config = api.getNormalizedConfig();
51
+ const relativeDistPath = config.output?.distPath?.root || 'dist';
52
+ const bff = config.bff;
53
+ const prefix = getPrimaryPrefix(bff?.prefix);
54
+ const apiRouter = new ApiRouter({
55
+ apiDir: apiDirectory,
56
+ appDir: appDirectory,
57
+ lambdaDir: lambdaDirectory,
58
+ prefix,
59
+ httpMethodDecider: bff?.httpMethodDecider,
60
+ isBuild: true
125
61
  });
126
- const handleCrossProjectInvocation = (isBuild = false)=>Promise.resolve().then(()=>{
127
- const { bff } = api.getNormalizedConfig();
128
- if (bff?.crossProject !== true) return;
129
- return isBuild ? generate() : compileApi().then(()=>generate());
62
+ const lambdaDir = apiRouter.getLambdaDir();
63
+ const packageJson = await fs.readJSON(upath.resolve(appDirectory, 'package.json'));
64
+ const generation = {
65
+ appDirectory,
66
+ apiDirectory,
67
+ lambdaDirectory: lambdaDir,
68
+ existLambda: apiRouter.isExistLambda(),
69
+ apiFiles: apiRouter.getApiFiles(),
70
+ relativeDistPath,
71
+ prefix,
72
+ port,
73
+ requestId: bff?.requestId || packageJson.name || packageName || 'default',
74
+ requestCreator: bff?.requestCreator,
75
+ httpMethodDecider: bff?.httpMethodDecider
76
+ };
77
+ const pluginOptions = {
78
+ prefix,
79
+ appDirectory,
80
+ relativeDistPath,
81
+ relativeApiPath: upath.relative(appDirectory, apiDirectory),
82
+ relativeLambdaPath: upath.relative(appDirectory, lambdaDir)
83
+ };
84
+ const runtimeOptions = {
85
+ runtime: bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST,
86
+ appDirectory,
87
+ relativeDistPath
88
+ };
89
+ const hooks = api.getHooks();
90
+ await clientGenerator({
91
+ prefix,
92
+ appDir: appDirectory,
93
+ apiDir: apiDirectory,
94
+ lambdaDir,
95
+ existLambda: generation.existLambda,
96
+ apiFiles: [
97
+ ...generation.apiFiles
98
+ ],
99
+ port,
100
+ requestId: generation.requestId,
101
+ requestCreator: generation.requestCreator,
102
+ clientCodegenPlugin: bff?.clientCodegenPlugin,
103
+ httpMethodDecider: generation.httpMethodDecider,
104
+ relativeDistPath,
105
+ relativeApiPath: pluginOptions.relativeApiPath
106
+ }, {
107
+ generation,
108
+ modifyArtifacts: async (context)=>{
109
+ const result = await hooks.modifyBffClientArtifacts.call(context);
110
+ if (result.generation !== generation) throw new Error('modifyBffClientArtifacts must preserve generation identity.');
111
+ return result;
112
+ },
113
+ beforePublish: async ()=>{
114
+ const entries = await hooks.modifyBffGeneratedEntries.call({
115
+ generation,
116
+ plugin: await renderBffPlugin(pluginOptions),
117
+ runtime: renderBffRuntime(runtimeOptions),
118
+ packageDependencies: {}
119
+ });
120
+ if (entries.generation !== generation) throw new Error('modifyBffGeneratedEntries must preserve generation identity.');
121
+ await pluginGenerator(pluginOptions, entries.plugin);
122
+ await runtimeGenerator(runtimeOptions, entries.runtime);
123
+ return entries.packageDependencies;
124
+ }
130
125
  });
126
+ };
127
+ const handleCrossProjectInvocation = async (isBuild = false)=>{
128
+ if (api.getNormalizedConfig().bff?.crossProject !== true) return;
129
+ if (!isBuild) await compileApi();
130
+ await generate();
131
+ };
131
132
  return {
132
133
  compileApi,
133
134
  generate,
@@ -1,5 +1,6 @@
1
1
  import "node:module";
2
2
  import { ApiRouter } from "@modern-js/bff-core";
3
+ import { createAsyncHook } from "@modern-js/plugin";
3
4
  import { normalizeOutputPath } from "@modern-js/utils";
4
5
  import path from "path";
5
6
  import { createCompressConfig } from "./cli/compress.mjs";
@@ -11,13 +12,19 @@ import { dirname as __rspack_dirname } from "node:path";
11
12
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
12
13
  const bffPlugin = ()=>({
13
14
  name: '@modern-js/plugin-bff',
15
+ registryHooks: {
16
+ onBeforeBffCompile: createAsyncHook(),
17
+ onAfterBffCompile: createAsyncHook(),
18
+ modifyBffClientArtifacts: createAsyncHook(),
19
+ modifyBffGeneratedEntries: createAsyncHook()
20
+ },
14
21
  setup: (api)=>{
15
22
  {
16
23
  const appContext = api.getAppContext();
17
24
  const userRuntimeFramework = api.getConfig()?.bff?.runtimeFramework;
18
25
  api.updateAppContext({
19
26
  ...appContext,
20
- bffRuntimeFramework: 'hono' === userRuntimeFramework ? 'hono' : 'effect'
27
+ bffRuntimeFramework: userRuntimeFramework ?? 'hono'
21
28
  });
22
29
  }
23
30
  const { compileApi, handleCrossProjectInvocation } = createBffGenerator(api);
@@ -35,7 +42,7 @@ const bffPlugin = ()=>({
35
42
  compress
36
43
  },
37
44
  bundlerChain: (chain, { CHAIN_ID, isServer })=>{
38
- const { port, appDirectory, apiDirectory, lambdaDirectory, bffRuntimeFramework } = api.getAppContext();
45
+ const { port, appDirectory, apiDirectory, lambdaDirectory } = api.getAppContext();
39
46
  const modernConfig = api.getNormalizedConfig();
40
47
  const { bff } = modernConfig || {};
41
48
  const prefix = getPrimaryPrefix(bff?.prefix);
@@ -65,10 +72,8 @@ const bffPlugin = ()=>({
65
72
  requestId: bff?.requestId,
66
73
  target: name,
67
74
  requestCreator: bff?.requestCreator,
68
- httpMethodDecider,
69
- bffRuntimeFramework,
70
- effectEntry: bff?.effect?.entry,
71
- effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
75
+ clientCodegenPlugin: bff?.clientCodegenPlugin,
76
+ httpMethodDecider
72
77
  });
73
78
  }
74
79
  }
@@ -1,66 +1,14 @@
1
1
  import __rslib_shim_module__ from "node:module";
2
2
  const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
3
3
  import { generateClient } from "@modern-js/bff-core";
4
- import { bundleEffectWorkerRuntimeSource, generateEffectClientCode, generateEffectWorkerRuntimeWrapper, resolveEffectEntryFile } from "@modern-js/plugin-bff-extensions/effect-source-loader";
5
4
  import { logger } from "@modern-js/utils";
6
- import path from "path";
7
- const EFFECT_BFF_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
8
- const EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY = 'modern-bff-runtime-source';
9
5
  const createErrorModule = (message)=>`throw new Error(${JSON.stringify(message)});`;
10
- async function transformEffectRuntimeSource(source, filename) {
11
- const swc = await import("@swc/core");
12
- const result = await swc.transform(source, {
13
- filename,
14
- sourceMaps: false,
15
- jsc: {
16
- parser: {
17
- syntax: "typescript",
18
- tsx: filename.endsWith('.tsx') || filename.endsWith('.jsx')
19
- },
20
- target: 'es2024'
21
- },
22
- module: {
23
- type: 'es6'
24
- }
25
- });
26
- return result.code;
27
- }
28
6
  async function loader(source) {
29
7
  this.cacheable();
30
8
  const { resourcePath } = this;
31
9
  delete require.cache[resourcePath];
32
10
  const callback = this.async();
33
11
  const draftOptions = this.getOptions();
34
- const resourceQueries = new URLSearchParams(this.resourceQuery);
35
- const effectEntryFile = resolveEffectEntryFile({
36
- appDir: draftOptions.appDir,
37
- apiDir: draftOptions.apiDir,
38
- effectEntry: draftOptions.effectEntry
39
- });
40
- if ('effect' === draftOptions.bffRuntimeFramework && effectEntryFile && path.resolve(effectEntryFile) === path.resolve(resourcePath) && resourceQueries.has(EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY)) {
41
- const code = await bundleEffectWorkerRuntimeSource(await transformEffectRuntimeSource(source, resourcePath), resourcePath, this);
42
- callback(void 0, code);
43
- return;
44
- }
45
- if ('effect' === draftOptions.bffRuntimeFramework && effectEntryFile && path.resolve(effectEntryFile) === path.resolve(resourcePath) && resourceQueries.has(EFFECT_BFF_WORKER_RUNTIME_QUERY)) return void callback(void 0, await generateEffectWorkerRuntimeWrapper(this, draftOptions, resourcePath));
46
- if ('effect' === draftOptions.bffRuntimeFramework && effectEntryFile && path.resolve(effectEntryFile) === path.resolve(resourcePath)) {
47
- const code = await generateEffectClientCode({
48
- appDir: draftOptions.appDir,
49
- apiDir: draftOptions.apiDir,
50
- resourcePath,
51
- prefix: Array.isArray(draftOptions.prefix) ? draftOptions.prefix[0] : draftOptions.prefix,
52
- port: Number(draftOptions.port),
53
- target: draftOptions.target,
54
- requestId: draftOptions.requestId,
55
- requestCreator: draftOptions.requestCreator,
56
- httpMethodDecider: draftOptions.httpMethodDecider,
57
- dataPlatformBatch: draftOptions.effectDataPlatformBatch,
58
- onDependency: (dependency)=>this.addDependency(dependency)
59
- });
60
- if (code) return void callback(void 0, code);
61
- callback(new Error(`Failed to generate Effect client for ${resourcePath}`));
62
- return;
63
- }
64
12
  const warning = `The file ${resourcePath} is not allowed to be imported in src directory, only API definition files are allowed.`;
65
13
  if (!draftOptions.existLambda) {
66
14
  logger.warn(warning);
@@ -76,7 +24,9 @@ async function loader(source) {
76
24
  port: Number(draftOptions.port),
77
25
  source,
78
26
  resourcePath,
79
- httpMethodDecider: draftOptions.httpMethodDecider
27
+ httpMethodDecider: draftOptions.httpMethodDecider,
28
+ clientCodegenPlugin: draftOptions.clientCodegenPlugin,
29
+ requestId: draftOptions.requestId
80
30
  };
81
31
  const { lambdaDir } = draftOptions;
82
32
  if (!resourcePath.startsWith(lambdaDir)) {
@@ -87,8 +37,12 @@ async function loader(source) {
87
37
  if (draftOptions.fetcher) options.fetcher = draftOptions.fetcher;
88
38
  if (draftOptions.requestCreator) options.requestCreator = draftOptions.requestCreator;
89
39
  options.requireResolve = require.resolve;
90
- const result = await generateClient(options);
91
- callback(void 0, result.isOk ? result.value : createErrorModule(result.value));
40
+ try {
41
+ const result = await generateClient(options);
42
+ callback(void 0, result.isOk ? result.value : createErrorModule(result.value));
43
+ } catch (error) {
44
+ callback(error instanceof Error ? error : new Error(String(error)));
45
+ }
92
46
  }
93
47
  const src_loader = loader;
94
48
  export default src_loader;
@@ -1,2 +1,2 @@
1
1
  import "node:module";
2
- export { configure, createRequest, createRequestContextHeaders, createRequestContextSnapshot, createUploader } from "@modern-js/create-request";
2
+ export { configure, createRequest, createUploader } from "@modern-js/create-request";
@@ -1,7 +1,4 @@
1
1
  import "node:module";
2
- import { resolveAdapterCrossProjectPolicy } from "@modern-js/plugin-bff-extensions/cross-project-policy";
3
- import { bindHonoRouteHandlers } from "@modern-js/plugin-bff-extensions/hono";
4
- import { logger } from "@modern-js/utils";
5
2
  import createHonoRoutes from "../../utils/createHonoRoutes.mjs";
6
3
  const before = [
7
4
  'custom-server-hook',
@@ -9,7 +6,8 @@ const before = [
9
6
  'render'
10
7
  ];
11
8
  class HonoAdapter {
12
- constructor(api){
9
+ constructor(api, createRouteBinder){
10
+ this.createRouteBinder = createRouteBinder;
13
11
  this.apiMiddleware = [];
14
12
  this.isHono = true;
15
13
  this.setHandlers = async ()=>{
@@ -17,25 +15,23 @@ class HonoAdapter {
17
15
  const { apiHandlerInfos } = this.api.getServerContext();
18
16
  const handlers = apiHandlerInfos ?? [];
19
17
  const honoHandlers = createHonoRoutes(handlers);
20
- const policy = resolveAdapterCrossProjectPolicy(this.api, handlers);
18
+ const bindRoute = this.createRouteBinder?.(this.api, handlers);
19
+ if (this.createRouteBinder && 'function' != typeof bindRoute) throw new TypeError('Hono route binder factory must return a function.');
21
20
  this.apiMiddleware = honoHandlers.map(({ path, method, handler })=>({
22
21
  name: 'hono-bff-api',
23
22
  path,
24
23
  method,
25
- handler: bindHonoRouteHandlers({
24
+ handler: bindRoute ? bindRoute({
26
25
  handler,
27
- policy,
28
- routePath: path,
29
- onError: this.api.getServerConfig()?.onError,
30
- reportError: (error)=>logger.error(error)
31
- }),
26
+ routePath: path
27
+ }) : handler,
32
28
  order: 'post',
33
29
  before: before
34
30
  }));
35
31
  };
36
32
  this.registerMiddleware = async (_options)=>{
37
33
  const { bffRuntimeFramework } = this.api.getServerContext();
38
- if ('hono' !== bffRuntimeFramework) {
34
+ if (void 0 !== bffRuntimeFramework && 'hono' !== bffRuntimeFramework) {
39
35
  this.isHono = false;
40
36
  return;
41
37
  }
@@ -1,19 +1,17 @@
1
1
  import "node:module";
2
2
  import { ApiRouter } from "@modern-js/bff-core";
3
- import { API_DIR, isFunction, isWebOnly } from "@modern-js/utils";
3
+ import { API_DIR, compatibleRequire, isFunction, isWebOnly, tryResolve } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  import { HonoAdapter } from "./runtime/hono/adapter.mjs";
6
- const RUNTIME_ADAPTER_LOADERS = {
7
- hono: async (api)=>[
8
- new HonoAdapter(api)
9
- ],
10
- effect: async (api)=>{
11
- const { EffectAdapter } = await import("@modern-js/plugin-bff-extensions/effect-adapter");
12
- return [
13
- new EffectAdapter(api)
14
- ];
15
- }
16
- };
6
+ async function loadRuntimeAdapters(specifier, api) {
7
+ if ('string' != typeof specifier || '' === specifier.trim()) throw new Error('The BFF runtime adapter must be a module specifier.');
8
+ const context = api.getServerContext();
9
+ const module = await compatibleRequire(tryResolve(specifier, context.appDirectory || context.distDirectory || process.cwd()), false);
10
+ if (!module || 'function' != typeof module.createRuntimeAdapters) throw new Error(`BFF runtime adapter module "${specifier}" must export createRuntimeAdapters.`);
11
+ const adapters = await module.createRuntimeAdapters(api);
12
+ if (!Array.isArray(adapters) || 0 === adapters.length || adapters.some((adapter)=>!adapter || 'function' != typeof adapter.registerMiddleware)) throw new Error(`BFF runtime adapter module "${specifier}" returned invalid adapters.`);
13
+ return adapters;
14
+ }
17
15
  const normalizePrefixList = (prefix)=>{
18
16
  if (Array.isArray(prefix)) return prefix.filter(Boolean);
19
17
  return [
@@ -21,21 +19,21 @@ const normalizePrefixList = (prefix)=>{
21
19
  ];
22
20
  };
23
21
  const getPrimaryPrefix = (prefix)=>normalizePrefixList(prefix)[0] || '/api';
24
- function resolveRuntimeFramework(runtimeFramework) {
25
- return 'hono' === runtimeFramework ? 'hono' : 'effect';
26
- }
27
22
  class Storage {
28
23
  constructor(){
29
24
  this.middlewares = [];
30
25
  }
31
26
  }
32
- const server = ()=>({
27
+ const server = (options = {})=>({
33
28
  name: '@modern-js/plugin-bff',
34
29
  setup: (api)=>{
35
30
  const storage = new Storage();
36
31
  let apiRouter = null;
37
32
  const appContext = api.getServerContext();
38
- const runtimeFramework = resolveRuntimeFramework(appContext.bffRuntimeFramework);
33
+ const runtimeFramework = appContext.bffRuntimeFramework ?? 'hono';
34
+ if (options.runtimeAdapters && Object.hasOwn(options.runtimeAdapters, 'hono')) throw new Error('The native Hono BFF adapter cannot be overridden.');
35
+ const adapterModule = options.runtimeAdapters?.[runtimeFramework];
36
+ if ('hono' !== runtimeFramework && (!options.runtimeAdapters || !Object.hasOwn(options.runtimeAdapters, runtimeFramework) || 'string' != typeof adapterModule || '' === adapterModule.trim())) throw new Error(`No BFF runtime adapter is registered for "${runtimeFramework}".`);
39
37
  api.onPrepare(async ()=>{
40
38
  const appContext = api.getServerContext();
41
39
  const { render } = appContext;
@@ -81,9 +79,19 @@ const server = ()=>({
81
79
  ]
82
80
  });
83
81
  }
84
- const runtimeAdapters = await RUNTIME_ADAPTER_LOADERS[runtimeFramework](api);
82
+ let createHonoRouteBinder;
83
+ if ('hono' === runtimeFramework && void 0 !== options.honoRouteBinder) {
84
+ if ('string' != typeof options.honoRouteBinder || '' === options.honoRouteBinder.trim()) throw new Error('The Hono BFF route binder must be a module specifier.');
85
+ const context = api.getServerContext();
86
+ const module = await compatibleRequire(tryResolve(options.honoRouteBinder, context.appDirectory || context.distDirectory || process.cwd()), false);
87
+ if (!module || 'function' != typeof module.createHonoRouteBinder) throw new Error(`BFF route binder module "${options.honoRouteBinder}" must export createHonoRouteBinder.`);
88
+ createHonoRouteBinder = module.createHonoRouteBinder;
89
+ }
90
+ const runtimeAdapters = 'hono' === runtimeFramework ? [
91
+ new HonoAdapter(api, createHonoRouteBinder)
92
+ ] : await loadRuntimeAdapters(adapterModule, api);
85
93
  await Promise.all(runtimeAdapters.map((adapter)=>adapter.registerMiddleware({
86
- prefix: 'effect' === runtimeFramework ? prefixList : prefix,
94
+ prefix: 'hono' === runtimeFramework ? prefix : prefixList,
87
95
  enableHandleWeb
88
96
  })));
89
97
  });
@@ -19,7 +19,7 @@ function createFileDetails(options) {
19
19
  const relativePath = path.relative(baseDirectory, resourcePath);
20
20
  const parsedPath = path.parse(relativePath);
21
21
  const targetDir = posixJoin(`./${relativeDistPath}/${CLIENT_DIR}`, parsedPath.dir, `${parsedPath.name}.js`);
22
- const absTargetDir = path.resolve(targetDir);
22
+ const absTargetDir = path.resolve(appDirectory, targetDir);
23
23
  const relativePathFromAppDirectory = path.relative(appDirectory, path.dirname(resourcePath));
24
24
  const typesFilePath = posixJoin(`./${relativeDistPath}`, relativePathFromAppDirectory, `${parsedPath.name}.d.ts`);
25
25
  return {