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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) 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/index.d.ts +1 -1
  42. package/dist/types/loader.d.ts +1 -11
  43. package/dist/types/runtime/create-request/index.d.ts +1 -3
  44. package/dist/types/runtime/hono/adapter.d.ts +9 -2
  45. package/dist/types/runtime/hono/index.d.ts +1 -1
  46. package/dist/types/server.d.ts +9 -2
  47. package/dist/types/utils/client-generator/generate.d.ts +8 -12
  48. package/dist/types/utils/client-generator/index.d.ts +8 -8
  49. package/dist/types/utils/client-generator/write-package.d.ts +1 -1
  50. package/dist/types/utils/clientGenerator.d.ts +2 -2
  51. package/dist/types/utils/crossProjectApiPlugin.d.ts +18 -12
  52. package/dist/types/utils/pluginGenerator.d.ts +6 -7
  53. package/dist/types/utils/runtimeGenerator.d.ts +6 -3
  54. package/package.json +33 -126
  55. package/dist/cjs/cli/serverGlobalVars.js +0 -113
  56. package/dist/cjs/runtime/data-platform/index.js +0 -62
  57. package/dist/cjs/runtime/effect/edge-dispatcher.js +0 -62
  58. package/dist/cjs/runtime/effect/edge.js +0 -69
  59. package/dist/cjs/runtime/effect/index.js +0 -305
  60. package/dist/cjs/runtime/effect-client/index.js +0 -62
  61. package/dist/cjs/runtime/effect-client/runtime.js +0 -62
  62. package/dist/esm/cli/serverGlobalVars.mjs +0 -72
  63. package/dist/esm/runtime/data-platform/index.mjs +0 -1
  64. package/dist/esm/runtime/effect/edge-dispatcher.mjs +0 -1
  65. package/dist/esm/runtime/effect/edge.mjs +0 -2
  66. package/dist/esm/runtime/effect/index.mjs +0 -16
  67. package/dist/esm/runtime/effect-client/index.mjs +0 -1
  68. package/dist/esm/runtime/effect-client/runtime.mjs +0 -1
  69. package/dist/esm-node/cli/serverGlobalVars.mjs +0 -73
  70. package/dist/esm-node/runtime/data-platform/index.mjs +0 -2
  71. package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +0 -2
  72. package/dist/esm-node/runtime/effect/edge.mjs +0 -3
  73. package/dist/esm-node/runtime/effect/index.mjs +0 -17
  74. package/dist/esm-node/runtime/effect-client/index.mjs +0 -2
  75. package/dist/esm-node/runtime/effect-client/runtime.mjs +0 -2
  76. package/dist/types/cli/serverGlobalVars.d.ts +0 -6
  77. package/dist/types/runtime/data-platform/index.d.ts +0 -1
  78. package/dist/types/runtime/effect/edge-dispatcher.d.ts +0 -1
  79. package/dist/types/runtime/effect/edge.d.ts +0 -2
  80. package/dist/types/runtime/effect/index.d.ts +0 -15
  81. package/dist/types/runtime/effect-client/index.d.ts +0 -1
  82. package/dist/types/runtime/effect-client/runtime.d.ts +0 -1
@@ -1,20 +1,19 @@
1
1
  import "node:module";
2
- import { generateClient } from "@modern-js/bff-core";
2
+ import { ClientCodegenError, generateClient } from "@modern-js/bff-core";
3
3
  import { fs, logger } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  import { CLIENT_DIR, createFileDetails, readDirectoryFiles, writeTargetFile } from "./files.mjs";
6
6
  import { getPackageName } from "./package-json.mjs";
7
7
  import { DEFAULT_EXPORT_RE, buildClientTypeFacade, createMissingClientDeclarationError, isMissingClientDeclarationError } from "./type-facade.mjs";
8
8
  import { setPackage, writeClientModuleBoundary } from "./write-package.mjs";
9
- async function clientGenerator(draftOptions) {
9
+ async function clientGenerator(draftOptions, integration) {
10
10
  const generatedClientDir = path.resolve(draftOptions.appDir, draftOptions.relativeDistPath, CLIENT_DIR);
11
11
  await fs.remove(generatedClientDir);
12
- const requestId = getPackageName(draftOptions.appDir) || process.env.npm_package_name;
12
+ const requestId = draftOptions.requestId || getPackageName(draftOptions.appDir) || process.env.npm_package_name;
13
13
  const lambdaSourceList = draftOptions.existLambda ? await readDirectoryFiles(draftOptions.appDir, draftOptions.lambdaDir, draftOptions.relativeDistPath, draftOptions.apiFiles) : [];
14
14
  const generatedSourceList = [
15
15
  ...lambdaSourceList
16
16
  ];
17
- let generatedEffectClient = null;
18
17
  const getClitentCode = async (resourcePath, source)=>{
19
18
  const warning = `The file ${resourcePath} is not allowed to be imported in src directory, only API definition files are allowed.`;
20
19
  if (!draftOptions.existLambda) return void logger.warn(warning);
@@ -29,6 +28,7 @@ async function clientGenerator(draftOptions) {
29
28
  target: 'bundle',
30
29
  httpMethodDecider: draftOptions.httpMethodDecider,
31
30
  requestCreator: draftOptions.requestCreator,
31
+ clientCodegenPlugin: draftOptions.clientCodegenPlugin,
32
32
  requestId
33
33
  };
34
34
  const { lambdaDir } = draftOptions;
@@ -37,9 +37,9 @@ async function clientGenerator(draftOptions) {
37
37
  return result;
38
38
  };
39
39
  const writeClientTypeFacade = async (source, clientCode)=>{
40
- if (!await fs.pathExists(path.resolve(source.relativeTargetDistDir))) throw createMissingClientDeclarationError(source.resourcePath, source.relativeTargetDistDir);
41
- const clientTypesFile = source.targetDir.replace(/\.js$/, '.d.ts');
42
- await writeTargetFile(path.resolve(clientTypesFile), buildClientTypeFacade(clientTypesFile, source.relativeTargetDistDir, DEFAULT_EXPORT_RE.test(clientCode), true));
40
+ if (!await fs.pathExists(path.resolve(draftOptions.appDir, source.relativeTargetDistDir))) throw createMissingClientDeclarationError(source.resourcePath, path.resolve(draftOptions.appDir, source.relativeTargetDistDir));
41
+ const clientTypesFile = source.absTargetDir.replace(/\.js$/, '.d.ts');
42
+ await writeTargetFile(path.resolve(clientTypesFile), buildClientTypeFacade(clientTypesFile, path.resolve(draftOptions.appDir, source.relativeTargetDistDir), DEFAULT_EXPORT_RE.test(clientCode), true));
43
43
  };
44
44
  try {
45
45
  for (const source of lambdaSourceList){
@@ -49,50 +49,51 @@ async function clientGenerator(draftOptions) {
49
49
  await writeClientTypeFacade(source, code.value);
50
50
  }
51
51
  }
52
- if ('effect' === draftOptions.bffRuntimeFramework) {
53
- const { generateEffectClient, resolveEffectEntryFile } = await import("@modern-js/plugin-bff-extensions/client-generator");
54
- const effectEntryFile = resolveEffectEntryFile({
55
- appDir: draftOptions.appDir,
56
- apiDir: draftOptions.apiDir,
57
- effectEntry: draftOptions.effectEntry
58
- });
59
- if (effectEntryFile) {
60
- const effectSource = await fs.readFile(effectEntryFile, 'utf8');
61
- const effectFileDetails = createFileDetails({
62
- appDirectory: draftOptions.appDir,
63
- baseDirectory: draftOptions.apiDir,
64
- resourcePath: effectEntryFile,
65
- source: effectSource,
66
- relativeDistPath: draftOptions.relativeDistPath
67
- });
68
- generatedEffectClient = await generateEffectClient({
69
- appDir: draftOptions.appDir,
70
- apiDir: draftOptions.apiDir,
71
- resourcePath: effectEntryFile,
72
- prefix: Array.isArray(draftOptions.prefix) ? draftOptions.prefix[0] : draftOptions.prefix,
73
- port: Number(draftOptions.port),
74
- target: 'bundle',
75
- requestId: draftOptions.requestId,
76
- requestCreator: draftOptions.requestCreator,
77
- httpMethodDecider: draftOptions.httpMethodDecider,
78
- dataPlatformBatch: draftOptions.effectDataPlatformBatch
79
- });
80
- if (generatedEffectClient) {
81
- const targetTypeFile = effectFileDetails.targetDir.replace(/\.js$/, '.d.ts');
82
- await writeTargetFile(effectFileDetails.absTargetDir, generatedEffectClient.code);
83
- await writeTargetFile(path.resolve(targetTypeFile), generatedEffectClient.declaration);
84
- generatedSourceList.push(effectFileDetails);
85
- }
86
- }
87
- }
88
52
  logger.info("Client bundle generate succeed");
89
53
  } catch (error) {
90
- if (isMissingClientDeclarationError(error)) throw error;
54
+ if (isMissingClientDeclarationError(error) || error instanceof ClientCodegenError) throw error;
91
55
  logger.error(`Client bundle generate failed: ${error}`);
92
56
  }
57
+ let packageDependencies;
58
+ if (integration) {
59
+ const artifacts = await integration.modifyArtifacts({
60
+ generation: integration.generation,
61
+ additionalArtifacts: []
62
+ });
63
+ if (artifacts.generation !== integration.generation) throw new Error('BFF artifacts must preserve generation identity.');
64
+ const sourcePaths = new Set(generatedSourceList.map((file)=>path.resolve(file.resourcePath)));
65
+ const exportKeys = new Set(generatedSourceList.map((file)=>file.exportKey));
66
+ const outputPaths = new Set(generatedSourceList.map((file)=>path.resolve(file.absTargetDir)));
67
+ const additionalFiles = artifacts.additionalArtifacts.map((artifact)=>{
68
+ const relative = artifact.sourcePath;
69
+ const resourcePath = path.resolve(draftOptions.apiDir, relative);
70
+ if (!relative || path.isAbsolute(relative) || relative.includes('\\') || path.relative(draftOptions.apiDir, resourcePath).split(path.sep).includes('..') || path.relative(draftOptions.apiDir, resourcePath).split(path.sep).join('/') !== relative || !/\.[cm]?[jt]sx?$/.test(relative)) throw new Error(`Invalid BFF client artifact source path: ${relative}`);
71
+ const file = createFileDetails({
72
+ appDirectory: draftOptions.appDir,
73
+ baseDirectory: draftOptions.apiDir,
74
+ resourcePath,
75
+ source: '',
76
+ relativeDistPath: draftOptions.relativeDistPath
77
+ });
78
+ if (sourcePaths.has(resourcePath) || exportKeys.has(file.exportKey) || outputPaths.has(path.resolve(file.absTargetDir))) throw new Error(`BFF client artifact collision: ${relative}`);
79
+ sourcePaths.add(resourcePath);
80
+ exportKeys.add(file.exportKey);
81
+ outputPaths.add(path.resolve(file.absTargetDir));
82
+ return {
83
+ file,
84
+ artifact
85
+ };
86
+ });
87
+ for (const { file, artifact } of additionalFiles){
88
+ await writeTargetFile(file.absTargetDir, artifact.code);
89
+ await writeTargetFile(file.absTargetDir.replace(/\.js$/, '.d.ts'), artifact.declaration);
90
+ generatedSourceList.push(file);
91
+ }
92
+ packageDependencies = await integration.beforePublish() || void 0;
93
+ }
93
94
  if (generatedSourceList.length > 0) await writeClientModuleBoundary(draftOptions.appDir, draftOptions.relativeDistPath);
94
- await setPackage(generatedSourceList, draftOptions.appDir, draftOptions.relativeDistPath);
95
- return generatedEffectClient;
95
+ await setPackage(generatedSourceList, draftOptions.appDir, draftOptions.relativeDistPath, packageDependencies);
96
+ return null;
96
97
  }
97
98
  const generate = clientGenerator;
98
99
  export default generate;
@@ -1,7 +1,7 @@
1
1
  import "node:module";
2
2
  import { fs } from "@modern-js/utils";
3
3
  import path from "path";
4
- import { EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, TYPE_PREFIX, toPosixPath } from "./files.mjs";
4
+ import { EXPORT_PREFIX, GENERATED_RUNTIME_DIRS, RUNTIME_DIR, TYPE_PREFIX, toPosixPath } from "./files.mjs";
5
5
  function getPackageName(appDirectory) {
6
6
  try {
7
7
  const packageJsonPath = path.resolve(appDirectory, './package.json');
@@ -47,7 +47,7 @@ function mergePackageJson(packageJson, files, typesVersion, exports, relativeDis
47
47
  }).map(([key])=>key);
48
48
  if (typeConflicts.length > 0) throw new Error(`[plugin-bff] package.json typesVersions conflict on keys: ${typeConflicts.sort().join(', ')}. Rename these keys or move them outside "${TYPE_PREFIX}" namespace.`);
49
49
  Object.keys(starTypes).forEach((key)=>{
50
- if (generatedTypeKeys.has(key) || key.startsWith(TYPE_PREFIX)) delete starTypes[key];
50
+ if (generatedTypeKeys.has(key) || key.startsWith(TYPE_PREFIX) || key === RUNTIME_DIR && isManagedTypeEntry(starTypes[key])) delete starTypes[key];
51
51
  });
52
52
  typesVersions['*'] = {
53
53
  ...starTypes,
@@ -64,7 +64,7 @@ function mergePackageJson(packageJson, files, typesVersion, exports, relativeDis
64
64
  }).map(([key])=>key);
65
65
  if (exportConflicts.length > 0) throw new Error(`[plugin-bff] package.json exports conflict on keys: ${exportConflicts.sort().join(', ')}. Rename these exports or move them outside "${EXPORT_PREFIX}" namespace.`);
66
66
  Object.keys(packageExports).forEach((key)=>{
67
- if (generatedExportKeys.has(key) || key.startsWith(EXPORT_PREFIX)) delete packageExports[key];
67
+ if (generatedExportKeys.has(key) || key.startsWith(EXPORT_PREFIX) || key === `./${RUNTIME_DIR}` && isManagedExportEntry(packageExports[key])) delete packageExports[key];
68
68
  });
69
69
  Object.assign(packageExports, exports);
70
70
  }
@@ -10,10 +10,19 @@ async function writeClientModuleBoundary(appDirectory, relativeDistPath) {
10
10
  type: 'module'
11
11
  }, null, 2)}\n`);
12
12
  }
13
- async function setPackage(files, appDirectory, relativeDistPath) {
13
+ async function setPackage(files, appDirectory, relativeDistPath, packageDependencies = {}) {
14
14
  const packagePath = path.resolve(appDirectory, './package.json');
15
15
  const packageContent = await fs.readFile(packagePath, 'utf8');
16
16
  const packageJson = JSON.parse(packageContent);
17
+ for (const [name, specifier] of Object.entries(packageDependencies)){
18
+ if (!name || 'string' != typeof specifier || !specifier.trim()) throw new Error(`Invalid BFF SDK dependency: ${name}`);
19
+ const existing = packageJson.dependencies?.[name];
20
+ if (void 0 !== existing && existing !== specifier) throw new Error(`package.json dependency conflict for ${name}: ${existing} versus ${specifier}`);
21
+ }
22
+ if (Object.keys(packageDependencies).length > 0) packageJson.dependencies = {
23
+ ...packageJson.dependencies,
24
+ ...packageDependencies
25
+ };
17
26
  const sortedFiles = [
18
27
  ...files
19
28
  ].sort((a, b)=>a.exportKey.localeCompare(b.exportKey));
@@ -70,6 +79,14 @@ async function setPackage(files, appDirectory, relativeDistPath) {
70
79
  types: toPosixPath(`./${relativeDistPath}/${RUNTIME_DIR}/index.d.ts`)
71
80
  }
72
81
  });
82
+ if (0 === files.length) {
83
+ Reflect.deleteProperty(typesVersions['*'], `${API_DIR}/*`);
84
+ Reflect.deleteProperty(exports, `./${API_DIR}/*`);
85
+ }
86
+ if (!fs.existsSync(path.resolve(appDirectory, relativeDistPath, RUNTIME_DIR, 'index.js'))) {
87
+ Reflect.deleteProperty(typesVersions['*'], RUNTIME_DIR);
88
+ Reflect.deleteProperty(exports, `./${RUNTIME_DIR}`);
89
+ }
73
90
  mergePackageJson(packageJson, addFiles, typesVersions, exports, relativeDistPath);
74
91
  await fs.promises.writeFile(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
75
92
  }
@@ -1,69 +1,39 @@
1
1
  import "node:module";
2
2
  import path from "path";
3
- const PACKAGE_NAME = '{packageName}';
4
- const REQUEST_ID = '{requestId}';
5
- const PREFIX = '{prefix}';
6
- const API_DIR = '{apiDirectory}';
7
- const LAMBDA_DIR = '{lambdaDirectory}';
8
- const DIST_DIR = '{distDirectory}';
9
- const RUNTIME_FRAMEWORK = '{runtimeFramework}';
10
- const EFFECT_ENTRY = '{effectEntry}';
11
- const OPERATION_CONTRACTS_JSON = '{operationContracts}';
12
- const NODE_MODULES = 'node_modules';
13
- const crossProjectApiPlugin = ()=>({
3
+ const createCrossProjectApiPlugin = (options)=>({
14
4
  name: '@modern-js/plugin-independent-bff',
15
5
  post: [
16
6
  '@modern-js/plugin-bff'
17
7
  ],
18
8
  setup: (api)=>{
19
- api.modifyResolvedConfig((resolvedConfig)=>{
20
- const { appDirectory: originAppDirectory } = api.getAppContext();
21
- const sdkPath = path.join(originAppDirectory, NODE_MODULES, PACKAGE_NAME);
22
- const sdkDistPath = path.join(sdkPath, DIST_DIR);
23
- const effectEntry = EFFECT_ENTRY ? path.join(sdkDistPath, EFFECT_ENTRY) : void 0;
24
- const apiDirectory = effectEntry ? path.dirname(effectEntry) : path.join(sdkDistPath, API_DIR);
25
- const lambdaDirectory = path.resolve(sdkDistPath, LAMBDA_DIR);
9
+ api.modifyResolvedConfig(async (resolvedConfig)=>{
10
+ const { appDirectory } = api.getAppContext();
11
+ const sdkDirectory = path.join(appDirectory, 'node_modules', options.packageName);
12
+ const sdkDistDirectory = path.join(sdkDirectory, options.relativeDistPath);
13
+ const apiDirectory = path.join(sdkDistDirectory, options.relativeApiPath);
14
+ const lambdaDirectory = path.resolve(sdkDistDirectory, options.relativeLambdaPath);
15
+ const configuredPrefix = api.getConfig()?.bff?.prefix;
16
+ if (configuredPrefix) {
17
+ const isSamePrefix = Array.isArray(configuredPrefix) ? 1 === configuredPrefix.length && configuredPrefix[0] === options.prefix : configuredPrefix === options.prefix;
18
+ if (!isSamePrefix) throw new Error(`[${options.packageName}] Invalid bff.prefix for cross-project BFF. Detected "${configuredPrefix}", expected "${options.prefix}". Remove bff.prefix from the consumer app, or set it exactly to "${options.prefix}".`);
19
+ }
26
20
  api.updateAppContext({
27
21
  apiDirectory,
28
- lambdaDirectory,
29
- bffRuntimeFramework: RUNTIME_FRAMEWORK
22
+ lambdaDirectory
30
23
  });
31
- const config = api.getConfig();
32
- const configuredPrefix = config?.bff?.prefix;
33
- if (configuredPrefix) {
34
- const isSamePrefix = Array.isArray(configuredPrefix) ? 1 === configuredPrefix.length && configuredPrefix[0] === PREFIX : configuredPrefix === PREFIX;
35
- if (!isSamePrefix) throw new Error(`[${PACKAGE_NAME}] Invalid bff.prefix for cross-project BFF. Detected "${configuredPrefix}", expected "${PREFIX}". Remove bff.prefix from the consumer app, or set it exactly to "${PREFIX}".`);
36
- }
37
- const configuredRuntimeFramework = config?.bff?.runtimeFramework;
38
- if (configuredRuntimeFramework && configuredRuntimeFramework !== RUNTIME_FRAMEWORK) throw new Error(`[${PACKAGE_NAME}] Runtime framework mismatch for cross-project BFF. Detected "${configuredRuntimeFramework}", but producer SDK requires "${RUNTIME_FRAMEWORK}".`);
39
24
  resolvedConfig.bff ??= {};
40
- resolvedConfig.bff.prefix = PREFIX;
41
- resolvedConfig.bff.runtimeFramework = RUNTIME_FRAMEWORK;
42
- if (EFFECT_ENTRY) resolvedConfig.bff.effect = {
43
- ...resolvedConfig.bff.effect,
44
- entry: effectEntry
45
- };
25
+ resolvedConfig.bff.prefix = options.prefix;
46
26
  resolvedConfig.bff.isCrossProjectServer = true;
47
- resolvedConfig.bff.requestId = resolvedConfig.bff.requestId || config?.bff?.requestId || REQUEST_ID || PACKAGE_NAME || 'default';
48
- const generatedOperationContracts = "{operationContracts}" === OPERATION_CONTRACTS_JSON ? {} : JSON.parse(OPERATION_CONTRACTS_JSON);
49
- resolvedConfig.bff.crossProjectPolicy = {
50
- ...resolvedConfig.bff.crossProjectPolicy || {},
51
- enabled: resolvedConfig.bff.crossProjectPolicy?.enabled ?? true,
52
- requireEnvelope: resolvedConfig.bff.crossProjectPolicy?.requireEnvelope ?? true,
53
- requireOperationContext: resolvedConfig.bff.crossProjectPolicy?.requireOperationContext ?? true,
54
- requireOperationContextDetails: resolvedConfig.bff.crossProjectPolicy?.requireOperationContextDetails ?? true,
55
- requireOperationSchemaHash: resolvedConfig.bff.crossProjectPolicy?.requireOperationSchemaHash ?? true,
56
- requireOperationVersion: resolvedConfig.bff.crossProjectPolicy?.requireOperationVersion ?? true,
57
- allowUnknownOperations: resolvedConfig.bff.crossProjectPolicy?.allowUnknownOperations ?? false,
58
- expectedOperationContracts: {
59
- ...resolvedConfig.bff.crossProjectPolicy?.expectedOperationContracts || {},
60
- ...generatedOperationContracts
61
- }
62
- };
63
- return resolvedConfig;
27
+ return options.modifyResolvedConfig ? options.modifyResolvedConfig(resolvedConfig, {
28
+ sdkDirectory,
29
+ sdkDistDirectory,
30
+ apiDirectory,
31
+ lambdaDirectory,
32
+ api
33
+ }) : resolvedConfig;
64
34
  });
65
35
  }
66
36
  });
67
- const utils_crossProjectApiPlugin = crossProjectApiPlugin;
68
- export default utils_crossProjectApiPlugin;
69
- export { API_DIR, DIST_DIR, EFFECT_ENTRY, LAMBDA_DIR, OPERATION_CONTRACTS_JSON, PACKAGE_NAME, PREFIX, REQUEST_ID, RUNTIME_FRAMEWORK, crossProjectApiPlugin };
37
+ const crossProjectApiPlugin = createCrossProjectApiPlugin;
38
+ export default crossProjectApiPlugin;
39
+ export { createCrossProjectApiPlugin };
@@ -1,33 +1,28 @@
1
1
  import "node:module";
2
2
  import { fs, logger, normalizeToPosixPath } from "@modern-js/utils";
3
3
  import path from "path";
4
- import { API_DIR, DIST_DIR, EFFECT_ENTRY, LAMBDA_DIR, OPERATION_CONTRACTS_JSON, PACKAGE_NAME, PREFIX, REQUEST_ID, 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 __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
8
- function replaceContent(source, packageName, requestId, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts) {
9
- const updatedSource = source.replace(new RegExp(PACKAGE_NAME, 'g'), packageName).replace(new RegExp(REQUEST_ID, 'g'), requestId).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).replace(new RegExp(EFFECT_ENTRY, 'g'), normalizeToPosixPath(relativeEffectEntry)).replace(`'${OPERATION_CONTRACTS_JSON}'`, ()=>JSON.stringify(JSON.stringify(operationContracts)));
10
- return updatedSource;
4
+ async function renderBffPlugin(options) {
5
+ const packageJson = await fs.readJSON(path.resolve(options.appDirectory, 'package.json'));
6
+ const descriptor = {
7
+ packageName: packageJson.name,
8
+ prefix: options.prefix,
9
+ relativeDistPath: normalizeToPosixPath(options.relativeDistPath),
10
+ relativeApiPath: normalizeToPosixPath(options.relativeApiPath),
11
+ relativeLambdaPath: normalizeToPosixPath(options.relativeLambdaPath)
12
+ };
13
+ return {
14
+ code: 'module' === packageJson.type ? `import { createCrossProjectApiPlugin } from '@modern-js/plugin-bff/cross-project';\nexport const crossProjectApiPlugin = () => createCrossProjectApiPlugin(${JSON.stringify(descriptor)});\n` : `'use strict';\nconst { createCrossProjectApiPlugin } = require('@modern-js/plugin-bff/cross-project');\nexports.crossProjectApiPlugin = () => createCrossProjectApiPlugin(${JSON.stringify(descriptor)});\n`,
15
+ declaration: `import type { AppTools, CliPlugin } from '@modern-js/app-tools';
16
+ export declare const crossProjectApiPlugin: () => CliPlugin<AppTools>;
17
+ `
18
+ };
11
19
  }
12
- async function pluginGenerator({ prefix, appDirectory, requestId, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts }) {
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(__rspack_import_meta_dirname__, 'crossProjectApiPlugin.js'), 'utf8');
19
- const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, requestId, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts);
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
- }
20
+ async function pluginGenerator(options, rendered) {
21
+ const module = rendered ?? await renderBffPlugin(options);
22
+ const directory = path.resolve(options.appDirectory, options.relativeDistPath, 'plugin');
23
+ await fs.outputFile(path.join(directory, 'index.js'), module.code);
24
+ await fs.outputFile(path.join(directory, 'index.d.ts'), module.declaration);
25
+ logger.info('Api plugin generate succeed');
31
26
  }
32
- const utils_pluginGenerator = pluginGenerator;
33
- export default utils_pluginGenerator;
27
+ export default pluginGenerator;
28
+ export { renderBffPlugin };
@@ -1,79 +1,27 @@
1
- import __rslib_shim_module__ from "node:module";
2
- const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
3
- import { renderProducerRuntimeDefaults } from "@modern-js/plugin-bff-extensions/cross-project-generation";
1
+ import "node:module";
2
+ import { pathToFileURL } from "node:url";
4
3
  import { fs } from "@modern-js/utils";
5
4
  import path from "path";
6
- const getPackageName = (appDirectory)=>{
7
- try {
8
- const packageJsonPath = path.resolve(appDirectory, './package.json');
9
- const packageJson = require(packageJsonPath);
10
- return packageJson.name;
11
- } catch (error) {
12
- return;
13
- }
14
- };
15
- async function runtimeGenerator({ runtime, appDirectory, relativeDistPath, packageName }) {
16
- const pluginDir = path.resolve(appDirectory, `./${relativeDistPath}`, 'runtime');
17
- const requestId = packageName || getPackageName(appDirectory) || process.env.npm_package_name || 'default';
18
- const runtimeImportPath = JSON.stringify(runtime);
19
- const requestIdValue = JSON.stringify(requestId);
20
- const source = `'use strict'; const { configure: _configure } = require(${runtimeImportPath});
21
- const defaultSecureOptions = ${renderProducerRuntimeDefaults(requestIdValue)};
22
- const initProducerClient = (options) => {
23
- return _configure({
24
- ...defaultSecureOptions,
25
- ...options,
26
- identityBinding: {
27
- ...defaultSecureOptions.identityBinding,
28
- ...(options && options.identityBinding ? options.identityBinding : {}),
29
- },
30
- operationContract: {
31
- ...defaultSecureOptions.operationContract,
32
- ...(options && options.operationContract ? options.operationContract : {}),
33
- },
34
- });
35
- }
36
- const configure = initProducerClient;
37
- Object.defineProperty(exports, '__esModule', { value: true });
38
- exports.initProducerClient = initProducerClient;
39
- exports.configure = configure;
40
- `;
41
- const pluginPath = path.join(pluginDir, 'index.js');
42
- await fs.ensureFile(pluginPath);
43
- await fs.writeFile(pluginPath, source);
44
- const tsSource = `type ProducerRuntimeModule = typeof import(${runtimeImportPath});
45
- type ProducerClientOptions = ProducerRuntimeModule extends {
46
- configure: (options: infer TOptions) => unknown;
47
- }
48
- ? TOptions
49
- : {
50
- request?: typeof fetch;
51
- interceptor?: (request: typeof fetch) => typeof fetch;
52
- allowedHeaders?: string[];
53
- requireEnvelope?: boolean;
54
- allowCrossOriginEnvelope?: boolean;
55
- identityBinding?: {
56
- enabled?: boolean;
57
- strict?: boolean;
58
- protectedHeaders?: string[];
59
- };
60
- operationContract?: {
61
- enabled?: boolean;
62
- strict?: boolean;
63
- requireSchemaHash?: boolean;
64
- requireOperationVersion?: boolean;
65
- };
66
- setDomain?: (ops?: {
67
- target: 'server' | 'browser';
68
- requestId?: string;
69
- }) => string;
70
- requestId?: string;
71
- };
72
- export declare const initProducerClient: (options?: ProducerClientOptions) => ReturnType<ProducerRuntimeModule['configure']>;
73
- export declare const configure: typeof initProducerClient;`;
74
- const pluginTypePath = path.join(pluginDir, 'index.d.ts');
75
- await fs.ensureFile(pluginTypePath);
76
- await fs.writeFile(pluginTypePath, tsSource);
5
+ function renderBffRuntime(options) {
6
+ const packagePath = path.resolve(options.appDirectory, 'package.json');
7
+ const packageJson = fs.existsSync(packagePath) ? fs.readJSONSync(packagePath) : {};
8
+ const requestId = options.packageName || packageJson.name || process.env.npm_package_name || 'default';
9
+ const runtime = JSON.stringify(options.runtime);
10
+ const esmRuntime = JSON.stringify(path.isAbsolute(options.runtime) ? pathToFileURL(options.runtime).href : options.runtime);
11
+ return {
12
+ code: 'module' === packageJson.type ? `import { configure as _configure } from ${esmRuntime};\nexport const initProducerClient = options => _configure({ requestId: ${JSON.stringify(requestId)}, ...options });\nexport const configure = initProducerClient;\n` : `'use strict';\nconst { configure: _configure } = require(${runtime});\nconst initProducerClient = options => _configure({ requestId: ${JSON.stringify(requestId)}, ...options });\nexports.initProducerClient = initProducerClient;\nexports.configure = initProducerClient;\n`,
13
+ declaration: `type Runtime = typeof import(${runtime});
14
+ export declare const initProducerClient: (options?: Parameters<Runtime['configure']>[0]) => ReturnType<Runtime['configure']>;
15
+ export declare const configure: typeof initProducerClient;
16
+ `
17
+ };
77
18
  }
78
- const utils_runtimeGenerator = runtimeGenerator;
79
- export default utils_runtimeGenerator;
19
+ async function runtimeGenerator(options, rendered) {
20
+ const directory = path.resolve(options.appDirectory, options.relativeDistPath, 'runtime');
21
+ if (null === rendered) return void await fs.remove(directory);
22
+ const module = rendered ?? renderBffRuntime(options);
23
+ await fs.outputFile(path.join(directory, 'index.js'), module.code);
24
+ await fs.outputFile(path.join(directory, 'index.d.ts'), module.declaration);
25
+ }
26
+ export default runtimeGenerator;
27
+ export { renderBffRuntime };
@@ -1,7 +1,7 @@
1
1
  import type { AppTools } from '@modern-js/app-tools';
2
2
  import type { CLIPluginAPI } from '@modern-js/plugin';
3
3
  export declare const createBffGenerator: (api: CLIPluginAPI<AppTools>) => {
4
- compileApi: () => Promise<void | undefined>;
4
+ compileApi: () => Promise<void>;
5
5
  generate: () => Promise<void>;
6
- handleCrossProjectInvocation: (isBuild?: boolean) => Promise<void | undefined>;
6
+ handleCrossProjectInvocation: (isBuild?: boolean) => Promise<void>;
7
7
  };
@@ -1 +1 @@
1
- export * from './constants';
1
+ export * from './constants.js';
@@ -9,20 +9,10 @@ export type APILoaderOptions = {
9
9
  port: number;
10
10
  fetcher?: string;
11
11
  requestCreator?: string;
12
+ clientCodegenPlugin?: string;
12
13
  requestId?: string;
13
14
  target: string;
14
15
  httpMethodDecider?: HttpMethodDecider;
15
- bffRuntimeFramework?: 'hono' | 'effect';
16
- effectEntry?: string;
17
- effectDataPlatformBatch?: {
18
- enabled?: boolean;
19
- endpoint?: string;
20
- flushIntervalMs?: number;
21
- maxBatchSize?: number;
22
- maxBatchBytes?: number;
23
- requestTimeoutMs?: number;
24
- allowedMethods?: string[];
25
- };
26
16
  };
27
17
  declare function loader(this: Rspack.LoaderContext<APILoaderOptions>, source: string): Promise<void>;
28
18
  export default loader;
@@ -1,3 +1 @@
1
- import { configure, createRequest, createRequestContextHeaders, createRequestContextSnapshot, createUploader } from '@modern-js/create-request';
2
- export type { OperationContext, OperationContextSource, RequestContextInput, RequestContextSnapshot, } from '@modern-js/create-request';
3
- export { configure, createRequest, createRequestContextHeaders, createRequestContextSnapshot, createUploader, };
1
+ export { configure, createRequest, createUploader, } from '@modern-js/create-request';
@@ -1,9 +1,16 @@
1
- import type { ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
1
+ import type { APIHandlerInfo } from '@modern-js/bff-core';
2
+ import type { MiddlewareHandler, ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
3
+ export type HonoRouteBinder = (options: {
4
+ handler: MiddlewareHandler | MiddlewareHandler[];
5
+ routePath: string;
6
+ }) => MiddlewareHandler | MiddlewareHandler[];
7
+ export type HonoRouteBinderFactory = (api: ServerPluginAPI, handlers: APIHandlerInfo[]) => HonoRouteBinder;
2
8
  export declare class HonoAdapter {
9
+ private readonly createRouteBinder?;
3
10
  apiMiddleware: ServerMiddleware[];
4
11
  api: ServerPluginAPI;
5
12
  isHono: boolean;
6
- constructor(api: ServerPluginAPI);
13
+ constructor(api: ServerPluginAPI, createRouteBinder?: HonoRouteBinderFactory | undefined);
7
14
  setHandlers: () => Promise<void>;
8
15
  /**
9
16
  * Register the BFF API routes as ordinary server middlewares.
@@ -1,2 +1,2 @@
1
1
  export * from '@modern-js/bff-core';
2
- export * from './operators';
2
+ export * from './operators.js';
@@ -1,3 +1,10 @@
1
- import type { ServerPlugin } from '@modern-js/server-core';
1
+ import type { BffUserConfig, ServerPlugin } from '@modern-js/server-core';
2
+ type RuntimeFramework = NonNullable<BffUserConfig['runtimeFramework']>;
3
+ export type BffServerPluginOptions = {
4
+ honoRouteBinder?: string;
5
+ runtimeAdapters?: {
6
+ [Runtime in RuntimeFramework]?: Runtime extends 'hono' ? never : string;
7
+ };
8
+ };
2
9
  export default _default;
3
- declare function _default(): ServerPlugin;
10
+ declare function _default(options?: BffServerPluginOptions): ServerPlugin;
@@ -1,3 +1,4 @@
1
+ import type { BffClientArtifacts, BffGeneration } from '@modern-js/app-tools';
1
2
  import type { HttpMethodDecider } from '@modern-js/types';
2
3
  export type APILoaderOptions = {
3
4
  prefix: string;
@@ -7,6 +8,7 @@ export type APILoaderOptions = {
7
8
  existLambda: boolean;
8
9
  port?: number;
9
10
  requestCreator?: string;
11
+ clientCodegenPlugin?: string;
10
12
  httpMethodDecider?: HttpMethodDecider;
11
13
  relativeDistPath: string;
12
14
  relativeApiPath: string;
@@ -19,17 +21,11 @@ export type APILoaderOptions = {
19
21
  * private files) never reach `generateClient`.
20
22
  */
21
23
  apiFiles: string[];
22
- bffRuntimeFramework?: 'hono' | 'effect';
23
- effectEntry?: string;
24
- effectDataPlatformBatch?: {
25
- enabled?: boolean;
26
- endpoint?: string;
27
- flushIntervalMs?: number;
28
- maxBatchSize?: number;
29
- maxBatchBytes?: number;
30
- requestTimeoutMs?: number;
31
- allowedMethods?: string[];
32
- };
33
24
  };
34
- export declare function clientGenerator(draftOptions: APILoaderOptions): Promise<import("@modern-js/plugin-bff-extensions/client-generator").GeneratedEffectClientArtifacts | null>;
25
+ export interface ClientGenerationIntegration {
26
+ generation: BffGeneration;
27
+ modifyArtifacts: (context: BffClientArtifacts) => Promise<BffClientArtifacts>;
28
+ beforePublish: () => Promise<Record<string, string> | void>;
29
+ }
30
+ export declare function clientGenerator(draftOptions: APILoaderOptions, integration?: ClientGenerationIntegration): Promise<null>;
35
31
  export default clientGenerator;
@@ -1,8 +1,8 @@
1
- export type { FileDetails } from './files';
2
- export { createFileDetails, readDirectoryFiles, writeTargetFile, } from './files';
3
- export type { APILoaderOptions } from './generate';
4
- export { clientGenerator, default } from './generate';
5
- export type { PackageJsonLike } from './package-json';
6
- export { getClientPackageName, getPackageName, mergePackageJson, } from './package-json';
7
- export { buildClientTypeFacade } from './type-facade';
8
- export { setPackage, writeClientModuleBoundary } from './write-package';
1
+ export type { FileDetails } from './files.js';
2
+ export { createFileDetails, readDirectoryFiles, writeTargetFile, } from './files.js';
3
+ export type { APILoaderOptions } from './generate.js';
4
+ export { clientGenerator, default } from './generate.js';
5
+ export type { PackageJsonLike } from './package-json.js';
6
+ export { getClientPackageName, getPackageName, mergePackageJson, } from './package-json.js';
7
+ export { buildClientTypeFacade } from './type-facade.js';
8
+ export { setPackage, writeClientModuleBoundary } from './write-package.js';
@@ -3,4 +3,4 @@ export declare function setPackage(files: {
3
3
  exportKey: string;
4
4
  targetDir: string;
5
5
  relativeTargetDistDir: string;
6
- }[], appDirectory: string, relativeDistPath: string): Promise<void>;
6
+ }[], appDirectory: string, relativeDistPath: string, packageDependencies?: Record<string, string>): Promise<void>;
@@ -1,2 +1,2 @@
1
- export * from './client-generator';
2
- export { default } from './client-generator';
1
+ export * from './client-generator/index.js';
2
+ export { default } from './client-generator/index.js';