@bleedingdev/modern-js-plugin-bff 3.5.0-ultramodern.98 → 3.8.1-ultramodern.1

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 (102) hide show
  1. package/dist/cjs/cli/compress.js +1 -1
  2. package/dist/cjs/cli/generator.js +106 -97
  3. package/dist/cjs/cli/serverGlobalVars.js +43 -57
  4. package/dist/cjs/cli.js +6 -2
  5. package/dist/cjs/loader.js +6 -5
  6. package/dist/cjs/runtime/effect/adapter/cross-project-policy.js +17 -12
  7. package/dist/cjs/runtime/effect/adapter/entry.js +13 -5
  8. package/dist/cjs/runtime/effect/adapter/error-response.js +11 -9
  9. package/dist/cjs/runtime/effect/adapter.js +2 -2
  10. package/dist/cjs/runtime/effect/backend-federation/load.js +26 -17
  11. package/dist/cjs/runtime/effect/backend-federation/plugin.js +12 -2
  12. package/dist/cjs/runtime/effect/backend-federation/remotes.js +5 -4
  13. package/dist/cjs/runtime/effect/backend-federation/runtime.js +80 -16
  14. package/dist/cjs/runtime/effect/edge-dispatcher.js +148 -0
  15. package/dist/cjs/runtime/effect/edge.js +19 -100
  16. package/dist/cjs/runtime/effect/endpoint-contracts.js +6 -1
  17. package/dist/cjs/runtime/effect/handler/definition.js +1 -0
  18. package/dist/cjs/runtime/effect/handler/http.js +17 -6
  19. package/dist/cjs/runtime/hono/adapter.js +32 -79
  20. package/dist/cjs/server.js +19 -30
  21. package/dist/cjs/utils/client-generator/write-package.js +1 -1
  22. package/dist/cjs/utils/crossProjectApiPlugin.js +21 -2
  23. package/dist/cjs/utils/effect-client-generator/generator.js +17 -16
  24. package/dist/cjs/utils/effect-client-generator/paths.js +4 -15
  25. package/dist/cjs/utils/effect-client-generator/rendering.js +8 -19
  26. package/dist/cjs/utils/effect-client-generator/runtime.js +15 -32
  27. package/dist/cjs/utils/effectSourceLoader.js +70 -10
  28. package/dist/cjs/utils/pluginGenerator.js +4 -4
  29. package/dist/esm/cli/compress.mjs +1 -1
  30. package/dist/esm/cli/generator.mjs +108 -98
  31. package/dist/esm/cli/serverGlobalVars.mjs +44 -48
  32. package/dist/esm/cli.mjs +2 -0
  33. package/dist/esm/loader.mjs +6 -5
  34. package/dist/esm/runtime/effect/adapter/cross-project-policy.mjs +17 -12
  35. package/dist/esm/runtime/effect/adapter/entry.mjs +13 -5
  36. package/dist/esm/runtime/effect/adapter/error-response.mjs +11 -9
  37. package/dist/esm/runtime/effect/adapter.mjs +3 -3
  38. package/dist/esm/runtime/effect/backend-federation/load.mjs +26 -17
  39. package/dist/esm/runtime/effect/backend-federation/plugin.mjs +12 -2
  40. package/dist/esm/runtime/effect/backend-federation/remotes.mjs +5 -4
  41. package/dist/esm/runtime/effect/backend-federation/runtime.mjs +80 -16
  42. package/dist/esm/runtime/effect/edge-dispatcher.mjs +38 -0
  43. package/dist/esm/runtime/effect/edge.mjs +1 -38
  44. package/dist/esm/runtime/effect/endpoint-contracts.mjs +6 -1
  45. package/dist/esm/runtime/effect/handler/definition.mjs +1 -0
  46. package/dist/esm/runtime/effect/handler/http.mjs +17 -6
  47. package/dist/esm/runtime/hono/adapter.mjs +33 -80
  48. package/dist/esm/server.mjs +18 -29
  49. package/dist/esm/utils/client-generator/write-package.mjs +1 -1
  50. package/dist/esm/utils/crossProjectApiPlugin.mjs +16 -3
  51. package/dist/esm/utils/effect-client-generator/generator.mjs +17 -16
  52. package/dist/esm/utils/effect-client-generator/paths.mjs +5 -6
  53. package/dist/esm/utils/effect-client-generator/rendering.mjs +9 -10
  54. package/dist/esm/utils/effect-client-generator/runtime.mjs +16 -23
  55. package/dist/esm/utils/effectSourceLoader.mjs +69 -12
  56. package/dist/esm/utils/pluginGenerator.mjs +5 -5
  57. package/dist/esm-node/cli/compress.mjs +1 -1
  58. package/dist/esm-node/cli/generator.mjs +108 -98
  59. package/dist/esm-node/cli/serverGlobalVars.mjs +44 -48
  60. package/dist/esm-node/cli.mjs +2 -0
  61. package/dist/esm-node/loader.mjs +6 -5
  62. package/dist/esm-node/runtime/effect/adapter/cross-project-policy.mjs +17 -12
  63. package/dist/esm-node/runtime/effect/adapter/entry.mjs +13 -5
  64. package/dist/esm-node/runtime/effect/adapter/error-response.mjs +11 -9
  65. package/dist/esm-node/runtime/effect/adapter.mjs +3 -3
  66. package/dist/esm-node/runtime/effect/backend-federation/load.mjs +26 -17
  67. package/dist/esm-node/runtime/effect/backend-federation/plugin.mjs +12 -2
  68. package/dist/esm-node/runtime/effect/backend-federation/remotes.mjs +5 -4
  69. package/dist/esm-node/runtime/effect/backend-federation/runtime.mjs +80 -16
  70. package/dist/esm-node/runtime/effect/edge-dispatcher.mjs +39 -0
  71. package/dist/esm-node/runtime/effect/edge.mjs +1 -38
  72. package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +6 -1
  73. package/dist/esm-node/runtime/effect/handler/definition.mjs +1 -0
  74. package/dist/esm-node/runtime/effect/handler/http.mjs +17 -6
  75. package/dist/esm-node/runtime/hono/adapter.mjs +33 -80
  76. package/dist/esm-node/server.mjs +18 -29
  77. package/dist/esm-node/utils/client-generator/write-package.mjs +1 -1
  78. package/dist/esm-node/utils/crossProjectApiPlugin.mjs +16 -3
  79. package/dist/esm-node/utils/effect-client-generator/generator.mjs +17 -16
  80. package/dist/esm-node/utils/effect-client-generator/paths.mjs +5 -6
  81. package/dist/esm-node/utils/effect-client-generator/rendering.mjs +9 -10
  82. package/dist/esm-node/utils/effect-client-generator/runtime.mjs +16 -23
  83. package/dist/esm-node/utils/effectSourceLoader.mjs +69 -12
  84. package/dist/esm-node/utils/pluginGenerator.mjs +5 -5
  85. package/dist/types/cli/generator.d.ts +2 -2
  86. package/dist/types/cli/serverGlobalVars.d.ts +4 -2
  87. package/dist/types/cli.d.ts +1 -0
  88. package/dist/types/runtime/effect/adapter/cross-project-policy.d.ts +1 -1
  89. package/dist/types/runtime/effect/backend-federation/runtime.d.ts +1 -1
  90. package/dist/types/runtime/effect/edge-dispatcher.d.ts +26 -0
  91. package/dist/types/runtime/effect/edge.d.ts +1 -26
  92. package/dist/types/runtime/effect/endpoint-contracts.d.ts +6 -0
  93. package/dist/types/runtime/effect/handler/definition.d.ts +2 -12
  94. package/dist/types/runtime/effect/handler/http.d.ts +3 -2
  95. package/dist/types/runtime/effect/handler/openapi.d.ts +1 -1
  96. package/dist/types/runtime/effect/handler/types.d.ts +10 -5
  97. package/dist/types/runtime/effect-client/index.d.ts +1 -1
  98. package/dist/types/runtime/hono/adapter.d.ts +14 -8
  99. package/dist/types/utils/crossProjectApiPlugin.d.ts +2 -0
  100. package/dist/types/utils/effectSourceLoader.d.ts +10 -0
  101. package/dist/types/utils/pluginGenerator.d.ts +3 -1
  102. package/package.json +44 -16
@@ -1,8 +1,7 @@
1
1
  import "node:module";
2
- import { ApiRouter } from "@modern-js/bff-core";
2
+ import { ApiRouter, buildOperationContractMap, deriveOperationVersion } from "@modern-js/bff-core";
3
3
  import { compile } from "@modern-js/server-utils";
4
- import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig } from "@modern-js/utils";
5
- import path from "path";
4
+ import { API_DIR, SHARED_DIR, fs, resolveServerTsconfig, upath } from "@modern-js/utils";
6
5
  import clientGenerator from "../utils/clientGenerator.mjs";
7
6
  import { bundleEffectEntryForNode } from "../utils/effectSourceLoader.mjs";
8
7
  import pluginGenerator from "../utils/pluginGenerator.mjs";
@@ -21,110 +20,121 @@ const effectEntryExtensions = [
21
20
  '.cts'
22
21
  ];
23
22
  function resolveEffectSourceEntry(appDirectory, apiDirectory, configuredEntry) {
24
- const entry = configuredEntry ? path.isAbsolute(configuredEntry) ? configuredEntry : path.resolve(appDirectory, configuredEntry) : path.resolve(apiDirectory, 'index');
25
- return path.extname(entry) ? entry : effectEntryExtensions.map((extension)=>`${entry}${extension}`).find((candidate)=>fs.existsSync(candidate));
23
+ const entry = void 0 !== configuredEntry && configuredEntry.length > 0 ? upath.isAbsolute(configuredEntry) ? configuredEntry : upath.resolve(appDirectory, configuredEntry) : upath.resolve(apiDirectory, 'index');
24
+ return upath.extname(entry).length > 0 ? entry : effectEntryExtensions.map((extension)=>`${entry}${extension}`).find((candidate)=>fs.existsSync(candidate));
26
25
  }
27
26
  function resolveBuiltEffectEntry(appDirectory, distDirectory, sourceEntry) {
28
- if (!sourceEntry) return;
29
- const relativeEntry = path.relative(appDirectory, sourceEntry);
30
- if ('..' === relativeEntry || relativeEntry.startsWith(`..${path.sep}`)) throw new Error(`Effect BFF entry must be inside the application directory: ${sourceEntry}`);
31
- const builtEntry = path.resolve(distDirectory, relativeEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
27
+ if (void 0 === sourceEntry || 0 === sourceEntry.length) return;
28
+ const relativeEntry = upath.relative(appDirectory, sourceEntry);
29
+ if ('..' === relativeEntry || relativeEntry.startsWith(`..${upath.sep}`)) throw new Error(`Effect BFF entry must be inside the application directory: ${sourceEntry}`);
30
+ const builtEntry = upath.resolve(distDirectory, relativeEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js');
32
31
  return fs.existsSync(builtEntry) ? builtEntry : void 0;
33
32
  }
34
33
  const createBffGenerator = (api)=>{
35
- const compileApi = async ()=>{
36
- const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
37
- const modernConfig = api.getNormalizedConfig();
38
- const distDir = path.resolve(distDirectory);
39
- const apiDir = apiDirectory || path.resolve(appDirectory, API_DIR);
40
- const sharedDir = sharedDirectory || path.resolve(appDirectory, SHARED_DIR);
41
- const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
42
- const sourceDirs = [];
43
- if (await fs.pathExists(apiDir)) sourceDirs.push(apiDir);
44
- if (await fs.pathExists(sharedDir)) sourceDirs.push(sharedDir);
45
- const { alias, globalVars } = modernConfig.source;
46
- const { alias: resolveAlias } = modernConfig.resolve;
47
- if (sourceDirs.length > 0) {
48
- const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
49
- const serializedGlobalVars = serializeServerGlobalVars(globalVars);
50
- await compile(appDirectory, {
51
- alias: combinedAlias
52
- }, {
53
- sourceDirs,
54
- distDir,
55
- tsconfigPath,
56
- moduleType,
57
- throwErrorInsteadOfExit: true
58
- });
59
- await transformServerGlobalVars(sourceDirs.map((sourceDir)=>path.resolve(distDir, path.relative(appDirectory, sourceDir))), serializedGlobalVars);
60
- if ('effect' === bffRuntimeFramework) {
61
- const sourceEntry = resolveEffectSourceEntry(appDirectory, apiDir, modernConfig.bff?.effect?.entry);
62
- const builtEntry = resolveBuiltEffectEntry(appDirectory, distDir, sourceEntry);
63
- if (!builtEntry) throw new Error(`Effect BFF entry was not emitted into ${distDir}: ${sourceEntry ?? path.resolve(apiDir, 'index')}`);
64
- await bundleEffectEntryForNode({
65
- appDir: appDirectory,
66
- entryPath: builtEntry,
67
- format: 'module' === moduleType ? 'esm' : 'cjs'
34
+ const compileApi = ()=>Promise.resolve().then(()=>{
35
+ const { appDirectory, distDirectory, apiDirectory, bffRuntimeFramework, sharedDirectory, moduleType } = api.getAppContext();
36
+ const modernConfig = api.getNormalizedConfig();
37
+ const distDir = upath.resolve(distDirectory);
38
+ const apiDir = apiDirectory || upath.resolve(appDirectory, API_DIR);
39
+ const sharedDir = sharedDirectory || upath.resolve(appDirectory, SHARED_DIR);
40
+ const tsconfigPath = resolveServerTsconfig(appDirectory, modernConfig?.server?.tsconfigPath);
41
+ const sourceDirs = [];
42
+ return fs.pathExists(apiDir).then((apiDirectoryExists)=>{
43
+ if (apiDirectoryExists) sourceDirs.push(apiDir);
44
+ return fs.pathExists(sharedDir);
45
+ }).then((sharedDirectoryExists)=>{
46
+ if (sharedDirectoryExists) sourceDirs.push(sharedDir);
47
+ if (0 === sourceDirs.length) return;
48
+ const { alias, globalVars } = modernConfig.source;
49
+ const { alias: resolveAlias } = modernConfig.resolve;
50
+ const combinedAlias = [].concat(alias ?? []).concat(resolveAlias ?? []);
51
+ const serializedGlobalVars = serializeServerGlobalVars(globalVars);
52
+ return compile(appDirectory, {
53
+ alias: combinedAlias
54
+ }, {
55
+ sourceDirs,
56
+ distDir,
57
+ tsconfigPath,
58
+ moduleType,
59
+ throwErrorInsteadOfExit: true
60
+ }).then(()=>transformServerGlobalVars(sourceDirs.map((sourceDir)=>upath.resolve(distDir, upath.relative(appDirectory, sourceDir))), serializedGlobalVars)).then(()=>{
61
+ if ('effect' !== bffRuntimeFramework) return;
62
+ const sourceEntry = resolveEffectSourceEntry(appDirectory, apiDir, modernConfig.bff?.effect?.entry);
63
+ const builtEntry = resolveBuiltEffectEntry(appDirectory, distDir, sourceEntry);
64
+ if (void 0 === builtEntry || 0 === builtEntry.length) throw new Error(`Effect BFF entry was not emitted into ${distDir}: ${sourceEntry ?? upath.resolve(apiDir, 'index')}`);
65
+ return bundleEffectEntryForNode({
66
+ appDir: appDirectory,
67
+ entryPath: builtEntry,
68
+ format: 'module' === moduleType ? 'esm' : 'cjs'
69
+ });
68
70
  });
69
- }
70
- }
71
- };
72
- const generate = async ()=>{
73
- const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework } = api.getAppContext();
74
- const modernConfig = api.getNormalizedConfig();
75
- const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
76
- const { bff } = modernConfig || {};
77
- const prefix = getPrimaryPrefix(bff?.prefix);
78
- const httpMethodDecider = bff?.httpMethodDecider;
79
- const apiRouter = new ApiRouter({
80
- apiDir: apiDirectory,
81
- appDir: appDirectory,
82
- lambdaDir: lambdaDirectory,
83
- prefix,
84
- httpMethodDecider,
85
- isBuild: true
86
- });
87
- const lambdaDir = apiRouter.getLambdaDir();
88
- const existLambda = apiRouter.isExistLambda();
89
- const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
90
- const relativeApiPath = path.relative(appDirectory, apiDirectory);
91
- const relativeLambdaPath = path.relative(appDirectory, lambdaDir);
92
- await pluginGenerator({
93
- prefix,
94
- appDirectory,
95
- relativeDistPath,
96
- relativeApiPath,
97
- relativeLambdaPath,
98
- runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect'
71
+ });
99
72
  });
100
- await clientGenerator({
101
- prefix,
102
- appDir: appDirectory,
103
- apiDir: apiDirectory,
104
- lambdaDir,
105
- existLambda,
106
- port,
107
- requestCreator: bff?.requestCreator,
108
- httpMethodDecider,
109
- relativeDistPath,
110
- relativeApiPath,
111
- bffRuntimeFramework,
112
- effectEntry: bff?.effect?.entry,
113
- effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
73
+ const generate = ()=>Promise.resolve().then(()=>{
74
+ const { appDirectory, apiDirectory, lambdaDirectory, port, bffRuntimeFramework, packageName } = api.getAppContext();
75
+ const modernConfig = api.getNormalizedConfig();
76
+ const relativeDistPath = modernConfig?.output?.distPath?.root || 'dist';
77
+ const { bff } = modernConfig || {};
78
+ const prefix = getPrimaryPrefix(bff?.prefix);
79
+ const httpMethodDecider = bff?.httpMethodDecider;
80
+ const apiRouter = new ApiRouter({
81
+ apiDir: apiDirectory,
82
+ appDir: appDirectory,
83
+ lambdaDir: lambdaDirectory,
84
+ prefix,
85
+ httpMethodDecider,
86
+ isBuild: true
87
+ });
88
+ const lambdaDir = apiRouter.getLambdaDir();
89
+ const existLambda = apiRouter.isExistLambda();
90
+ const packageJson = fs.readJSONSync(upath.resolve(appDirectory, 'package.json'));
91
+ const requestId = packageJson.name || packageName;
92
+ const operationContractsPromise = existLambda ? apiRouter.getApiHandlers().then((handlers)=>buildOperationContractMap({
93
+ handlers,
94
+ requestId,
95
+ operationVersion: deriveOperationVersion(packageJson.version)
96
+ })) : Promise.resolve({});
97
+ return operationContractsPromise.then((operationContracts)=>{
98
+ const runtime = bff?.runtimeCreateRequest || RUNTIME_CREATE_REQUEST;
99
+ const relativeApiPath = upath.relative(appDirectory, apiDirectory);
100
+ const relativeLambdaPath = upath.relative(appDirectory, lambdaDir);
101
+ const sourceEffectEntry = 'effect' === bffRuntimeFramework ? resolveEffectSourceEntry(appDirectory, apiDirectory, bff?.effect?.entry) : void 0;
102
+ const relativeEffectEntry = void 0 !== sourceEffectEntry && sourceEffectEntry.length > 0 ? upath.relative(appDirectory, sourceEffectEntry).replace(/\.(?:[cm]?ts|tsx|jsx)$/u, '.js') : '';
103
+ return pluginGenerator({
104
+ prefix,
105
+ appDirectory,
106
+ relativeDistPath,
107
+ relativeApiPath,
108
+ relativeLambdaPath,
109
+ runtimeFramework: 'hono' === bffRuntimeFramework ? 'hono' : 'effect',
110
+ relativeEffectEntry,
111
+ operationContracts
112
+ }).then(()=>clientGenerator({
113
+ prefix,
114
+ appDir: appDirectory,
115
+ apiDir: apiDirectory,
116
+ lambdaDir,
117
+ existLambda,
118
+ port,
119
+ requestCreator: bff?.requestCreator,
120
+ httpMethodDecider,
121
+ relativeDistPath,
122
+ relativeApiPath,
123
+ bffRuntimeFramework,
124
+ effectEntry: bff?.effect?.entry,
125
+ effectDataPlatformBatch: bff?.effect?.dataPlatform?.batch
126
+ })).then(()=>runtimeGenerator({
127
+ runtime,
128
+ appDirectory,
129
+ relativeDistPath
130
+ }));
131
+ });
114
132
  });
115
- await runtimeGenerator({
116
- runtime,
117
- appDirectory,
118
- relativeDistPath
133
+ const handleCrossProjectInvocation = (isBuild = false)=>Promise.resolve().then(()=>{
134
+ const { bff } = api.getNormalizedConfig();
135
+ if (bff?.crossProject !== true) return;
136
+ return isBuild ? generate() : compileApi().then(()=>generate());
119
137
  });
120
- };
121
- const handleCrossProjectInvocation = async (isBuild = false)=>{
122
- const { bff } = api.getNormalizedConfig();
123
- if (bff?.crossProject) {
124
- if (!isBuild) await compileApi();
125
- await generate();
126
- }
127
- };
128
138
  return {
129
139
  compileApi,
130
140
  generate,
@@ -1,25 +1,20 @@
1
1
  import "node:module";
2
- import { applyOptionsChain, fs } from "@modern-js/utils";
2
+ import { applyOptionsChain, fs, upath } from "@modern-js/utils";
3
3
  import { transform } from "@swc/core";
4
- import path from "path";
5
4
  const RELEASE_IDENTITY_GLOBAL_NAMES = [
6
5
  'ULTRAMODERN_BUILD_MARKER',
7
6
  'ULTRAMODERN_SOURCE_REVISION',
8
7
  'ULTRAMODERN_RELEASE_VERSION'
9
8
  ];
10
- const collectJavaScriptFiles = async (directory)=>{
11
- const entries = await fs.readdir(directory, {
12
- withFileTypes: true
13
- });
14
- const nested = await Promise.all(entries.map(async (entry)=>{
15
- const absolutePath = path.join(directory, entry.name);
16
- if (entry.isDirectory()) return collectJavaScriptFiles(absolutePath);
17
- return /\.(?:c|m)?js$/u.test(entry.name) ? [
18
- absolutePath
19
- ] : [];
20
- }));
21
- return nested.flat();
22
- };
9
+ const collectJavaScriptFiles = (directory)=>Promise.resolve().then(()=>fs.readdir(directory, {
10
+ withFileTypes: true
11
+ })).then((entries)=>Promise.all(entries.map((entry)=>{
12
+ const absolutePath = upath.join(directory, entry.name);
13
+ if (entry.isDirectory()) return collectJavaScriptFiles(absolutePath);
14
+ return /\.(?:c|m)?js$/u.test(entry.name) ? [
15
+ absolutePath
16
+ ] : [];
17
+ })).then((nested)=>nested.flat()));
23
18
  const serializeServerGlobalVars = (options)=>{
24
19
  const globalVars = applyOptionsChain({}, options, {
25
20
  env: 'server',
@@ -41,37 +36,38 @@ const serializeServerGlobalVars = (options)=>{
41
36
  ];
42
37
  }));
43
38
  };
44
- const transformServerGlobalVars = async (outputDirectories, globalVars)=>{
45
- if (0 === Object.keys(globalVars).length) return;
46
- const existingDirectories = (await Promise.all(outputDirectories.map(async (directory)=>await fs.pathExists(directory) ? directory : void 0))).filter((directory)=>Boolean(directory));
47
- const files = (await Promise.all(existingDirectories.map(collectJavaScriptFiles))).flat();
48
- const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`void ${globalVars[name]};`).join('')}\n` : '';
49
- await Promise.all(files.map(async (filename)=>{
50
- const source = await fs.readFile(filename, 'utf8');
51
- const sourceMapFilename = `${filename}.map`;
52
- const sourceMapExists = await fs.pathExists(sourceMapFilename);
53
- const inputSourceMap = sourceMapExists ? await fs.readFile(sourceMapFilename, 'utf8') : void 0;
54
- const result = await transform(`${releaseIdentityBanner}${source}`, {
55
- filename,
56
- inputSourceMap,
57
- sourceMaps: sourceMapExists,
58
- jsc: {
59
- parser: {
60
- syntax: "ecmascript"
61
- },
62
- preserveAllComments: true,
63
- target: 'es2022',
64
- transform: {
65
- optimizer: {
66
- globals: {
67
- vars: globalVars
68
- }
69
- }
70
- }
71
- }
72
- });
73
- await fs.writeFile(filename, result.code);
74
- if (sourceMapExists && result.map) await fs.writeFile(sourceMapFilename, result.map);
75
- }));
76
- };
39
+ const transformServerGlobalVars = (outputDirectories, globalVars)=>Promise.resolve().then(()=>{
40
+ if (0 === Object.keys(globalVars).length) return;
41
+ return Promise.all(outputDirectories.map((directory)=>fs.pathExists(directory).then((directoryExists)=>directoryExists ? directory : void 0))).then((existingDirectories)=>{
42
+ const filesPromise = Promise.all(existingDirectories.filter((directory)=>void 0 !== directory).map(collectJavaScriptFiles)).then((files)=>files.flat());
43
+ const releaseIdentityBanner = RELEASE_IDENTITY_GLOBAL_NAMES.every((name)=>Object.hasOwn(globalVars, name)) ? `${RELEASE_IDENTITY_GLOBAL_NAMES.map((name)=>`void ${globalVars[name]};`).join('')}\n` : '';
44
+ return filesPromise.then((files)=>Promise.all(files.map((filename)=>fs.readFile(filename, 'utf8').then((source)=>{
45
+ const sourceMapFilename = `${filename}.map`;
46
+ return fs.pathExists(sourceMapFilename).then((sourceMapExists)=>{
47
+ const inputSourceMapPromise = sourceMapExists ? fs.readFile(sourceMapFilename, 'utf8') : Promise.resolve(void 0);
48
+ return inputSourceMapPromise.then((inputSourceMap)=>transform(`${releaseIdentityBanner}${source}`, {
49
+ filename,
50
+ inputSourceMap,
51
+ sourceMaps: sourceMapExists,
52
+ jsc: {
53
+ parser: {
54
+ syntax: "ecmascript"
55
+ },
56
+ preserveAllComments: true,
57
+ target: 'es2022',
58
+ transform: {
59
+ optimizer: {
60
+ globals: {
61
+ vars: globalVars
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }).then((result)=>fs.writeFile(filename, result.code).then(()=>{
67
+ if (sourceMapExists && void 0 !== result.map && result.map.length > 0) return fs.writeFile(sourceMapFilename, result.map);
68
+ })));
69
+ });
70
+ }))));
71
+ }).then(()=>void 0);
72
+ });
77
73
  export { serializeServerGlobalVars, transformServerGlobalVars };
@@ -132,4 +132,6 @@ const bffPlugin = ()=>({
132
132
  });
133
133
  }
134
134
  });
135
+ const cli = bffPlugin;
136
+ export default cli;
135
137
  export { bffPlugin };
@@ -6,6 +6,7 @@ import path from "path";
6
6
  import { generateEffectClientCode, resolveEffectEntryFile } from "./utils/effectClientGenerator.mjs";
7
7
  const EFFECT_BFF_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
8
8
  const EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY = 'modern-bff-runtime-source';
9
+ const createErrorModule = (message)=>`throw new Error(${JSON.stringify(message)});`;
9
10
  async function transformEffectRuntimeSource(source, filename) {
10
11
  const swc = await import("@swc/core");
11
12
  const result = await swc.transform(source, {
@@ -27,7 +28,7 @@ async function transformEffectRuntimeSource(source, filename) {
27
28
  function createEffectWorkerRuntimeWrapper(resourcePath) {
28
29
  const sourceRequest = `${resourcePath}?${EFFECT_BFF_WORKER_RUNTIME_SOURCE_QUERY}`;
29
30
  return `import * as effectBffModule from ${JSON.stringify(sourceRequest)};
30
- import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge';
31
+ import { createEffectBffEdgeDispatcher } from '@modern-js/plugin-bff/effect-edge/dispatcher';
31
32
 
32
33
  export const __modern_create_effect_bff_dispatcher = options =>
33
34
  createEffectBffEdgeDispatcher({
@@ -68,13 +69,13 @@ async function loader(source) {
68
69
  onDependency: (dependency)=>this.addDependency(dependency)
69
70
  });
70
71
  if (code) return void callback(void 0, code);
71
- callback(void 0, `throw new Error('Failed to generate Effect client for ${resourcePath}')`);
72
+ callback(void 0, createErrorModule(`Failed to generate Effect client for ${resourcePath}`));
72
73
  return;
73
74
  }
74
75
  const warning = `The file ${resourcePath} is not allowed to be imported in src directory, only API definition files are allowed.`;
75
76
  if (!draftOptions.existLambda) {
76
77
  logger.warn(warning);
77
- callback(null, `throw new Error('${warning}')`);
78
+ callback(null, createErrorModule(warning));
78
79
  return;
79
80
  }
80
81
  const options = {
@@ -91,14 +92,14 @@ async function loader(source) {
91
92
  const { lambdaDir } = draftOptions;
92
93
  if (!resourcePath.startsWith(lambdaDir)) {
93
94
  logger.warn(warning);
94
- callback(null, `throw new Error('${warning}')`);
95
+ callback(null, createErrorModule(warning));
95
96
  return;
96
97
  }
97
98
  if (draftOptions.fetcher) options.fetcher = draftOptions.fetcher;
98
99
  if (draftOptions.requestCreator) options.requestCreator = draftOptions.requestCreator;
99
100
  options.requireResolve = require.resolve;
100
101
  const result = await generateClient(options);
101
- callback(void 0, result.isOk ? result.value : `throw new Error('${result.value}')`);
102
+ callback(void 0, result.isOk ? result.value : createErrorModule(result.value));
102
103
  }
103
104
  const src_loader = loader;
104
105
  export default src_loader;
@@ -3,22 +3,27 @@ import { logger } from "@modern-js/utils";
3
3
  import { HttpApi } from "effect/unstable/httpapi";
4
4
  import { resolveAdapterCrossProjectPolicy } from "../../../utils/crossProjectServerPolicy.mjs";
5
5
  import { collectEffectEndpoints, extractHttpApiFromModule, toOperationContractSources } from "../endpoint-contracts.mjs";
6
- async function resolveEffectAdapterCrossProjectPolicy(api, prefix, mod) {
7
- let contractSources = [];
8
- if (mod) try {
9
- const effectApi = await extractHttpApiFromModule(mod, HttpApi.isHttpApi);
10
- if (effectApi) {
11
- const reflect = (apiValue, handlers)=>HttpApi.reflect(apiValue, {
6
+ function resolveEffectAdapterCrossProjectPolicy(api, prefix, mod) {
7
+ const contractSourcesPromise = null === mod ? Promise.resolve([]) : Promise.resolve().then(()=>extractHttpApiFromModule(mod, HttpApi.isHttpApi)).then((effectApi)=>{
8
+ if (null !== effectApi) {
9
+ const reflect = (apiValue, handlers)=>{
10
+ if (!HttpApi.isHttpApi(apiValue)) throw new Error('[BFF][Effect] Endpoint reflection received a non-HttpApi value.');
11
+ HttpApi.reflect(apiValue, {
12
12
  onGroup: handlers.onGroup ?? (()=>{}),
13
13
  onEndpoint: handlers.onEndpoint
14
14
  });
15
- contractSources = toOperationContractSources(collectEffectEndpoints(reflect, effectApi, prefix));
15
+ };
16
+ return toOperationContractSources(collectEffectEndpoints(reflect, effectApi, prefix));
16
17
  }
17
- } catch (error) {
18
+ return [];
19
+ }).catch((error)=>{
18
20
  logger.warn(`[BFF][Effect] Failed reflect HttpApi endpoints cross-project policy: ${String(error)}`);
19
- }
20
- const policy = resolveAdapterCrossProjectPolicy(api, contractSources);
21
- if (policy?.enabled && 0 === contractSources.length) logger.warn('[BFF][Effect] Cross-project policy enabled but no HttpApi endpoints could be reflected; requests fail operation-contract matching unless allowUnknownOperations is enabled.');
22
- return policy;
21
+ return [];
22
+ });
23
+ return contractSourcesPromise.then((contractSources)=>{
24
+ const policy = resolveAdapterCrossProjectPolicy(api, contractSources);
25
+ if (policy?.enabled === true && 0 === contractSources.length) logger.warn('[BFF][Effect] Cross-project policy enabled but no HttpApi endpoints could be reflected; requests fail operation-contract matching unless allowUnknownOperations is enabled.');
26
+ return policy;
27
+ });
23
28
  }
24
29
  export { resolveEffectAdapterCrossProjectPolicy };
@@ -23,23 +23,31 @@ function relativeAppPath(appDirectory, entryPath) {
23
23
  const relativePath = path.relative(appDirectory, entryPath);
24
24
  return '..' === relativePath || relativePath.startsWith(`..${path.sep}`) ? void 0 : relativePath;
25
25
  }
26
+ function isDependencyPath(relativePath) {
27
+ return relativePath?.split(/[\\/]/u).includes('node_modules') ?? false;
28
+ }
26
29
  function resolveEffectAdapterEntryFile(api) {
27
30
  const { appDirectory, apiDirectory, distDirectory } = api.getServerContext();
28
31
  const appRoot = path.resolve(appDirectory || process.cwd());
29
- const productionRoot = isProd() && distDirectory ? distDirectory : void 0;
32
+ const productionRoot = isProd() && distDirectory ? path.resolve(distDirectory) : void 0;
30
33
  const bffConfig = api.getServerConfig()?.bff;
31
34
  const configuredEntry = bffConfig?.effect?.entry;
32
35
  if (configuredEntry) {
33
36
  const sourceEntry = path.isAbsolute(configuredEntry) ? configuredEntry : path.resolve(appRoot, configuredEntry);
34
37
  if (productionRoot) {
35
38
  const relativeEntry = relativeAppPath(appRoot, sourceEntry);
39
+ if (void 0 === relativeEntry || isDependencyPath(relativeEntry)) return resolveJsOrTsEntry(sourceEntry);
36
40
  return relativeEntry ? resolveJsOrTsEntry(builtEntryPath(path.resolve(productionRoot, relativeEntry))) : void 0;
37
41
  }
38
42
  return resolveJsOrTsEntry(sourceEntry);
39
43
  }
40
- const relativeApiDirectory = apiDirectory ? path.isAbsolute(apiDirectory) ? relativeAppPath(appRoot, apiDirectory) : apiDirectory : API_DIR;
41
- if (!relativeApiDirectory) return;
42
- const apiRoot = path.resolve(productionRoot || appRoot, relativeApiDirectory);
43
- return resolveJsOrTsEntry(path.resolve(apiRoot, 'index'));
44
+ const resolvedApiDirectory = path.resolve(appRoot, apiDirectory || API_DIR);
45
+ const productionApiDirectory = productionRoot ? void 0 !== relativeAppPath(productionRoot, resolvedApiDirectory) ? resolvedApiDirectory : (()=>{
46
+ const relativeApiDirectory = relativeAppPath(appRoot, resolvedApiDirectory);
47
+ if (isDependencyPath(relativeApiDirectory)) return resolvedApiDirectory;
48
+ return relativeApiDirectory ? path.resolve(productionRoot, relativeApiDirectory) : void 0;
49
+ })() : resolvedApiDirectory;
50
+ if (!productionApiDirectory) return;
51
+ return resolveJsOrTsEntry(path.resolve(productionApiDirectory, 'index'));
44
52
  }
45
53
  export { resolveEffectAdapterEntryFile };
@@ -1,23 +1,25 @@
1
1
  import "node:module";
2
2
  import { logger } from "@modern-js/utils";
3
3
  import { createSafeFailureResponse } from "../../safe-failure.mjs";
4
- async function createEffectAdapterRuntimeErrorResponse(api, error, c) {
4
+ function createEffectAdapterRuntimeErrorResponse(api, error, c) {
5
5
  try {
6
6
  const serverConfig = api.getServerConfig();
7
7
  const onErrorHandler = serverConfig?.onError;
8
- if (onErrorHandler) {
8
+ if (void 0 !== onErrorHandler) {
9
9
  const onErrorContext = ensureJsonContext(c);
10
- const result = await onErrorHandler(error instanceof Error ? error : new Error(String(error)), onErrorContext);
11
- if (result instanceof Response) return result;
12
- } else logger.error(error);
10
+ return Promise.resolve(onErrorHandler(error instanceof Error ? error : new Error(String(error)), onErrorContext)).then((result)=>result instanceof Response ? result : createSafeFailureResponse(error)).catch((configError)=>{
11
+ logger.error(`Error in serverConfig.onError handler: ${String(configError)}`);
12
+ return createSafeFailureResponse(error);
13
+ });
14
+ }
15
+ logger.error(error instanceof Error ? error : new Error(String(error)));
13
16
  } catch (configError) {
14
17
  logger.error(`Error in serverConfig.onError handler: ${configError}`);
15
18
  }
16
- return createSafeFailureResponse(error);
19
+ return Promise.resolve(createSafeFailureResponse(error));
17
20
  }
18
21
  function ensureJsonContext(c) {
19
- const maybeJsonContext = c;
20
- if ('function' == typeof maybeJsonContext.json) return c;
22
+ if ('json' in c && 'function' == typeof c.json) return c;
21
23
  const headers = {
22
24
  'content-type': 'application/json; charset=utf-8'
23
25
  };
@@ -28,7 +30,7 @@ function ensureJsonContext(c) {
28
30
  headers: extraHeaders
29
31
  } : statusOrInit;
30
32
  const responseHeaders = new Headers(headers);
31
- if (responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
33
+ if (void 0 !== responseInit.headers) new Headers(responseInit.headers).forEach((value, key)=>{
32
34
  responseHeaders.set(key, value);
33
35
  });
34
36
  return new Response(JSON.stringify(data), {
@@ -1,7 +1,7 @@
1
1
  import "node:module";
2
2
  import { fs, isProd, logger } from "@modern-js/utils";
3
3
  import path from "path";
4
- import { loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
4
+ import { loadEffectBuiltModule, loadEffectSourceModule } from "../../utils/effectSourceLoader.mjs";
5
5
  import { before } from "./adapter/constants.mjs";
6
6
  import { resolveEffectAdapterCrossProjectPolicy } from "./adapter/cross-project-policy.mjs";
7
7
  import { resolveEffectAdapterEntryFile } from "./adapter/entry.mjs";
@@ -30,10 +30,10 @@ class EffectAdapter {
30
30
  let mod;
31
31
  try {
32
32
  const { appDirectory } = this.api.getServerContext();
33
- mod = await loadEffectSourceModule({
33
+ mod = await (isProd() ? loadEffectBuiltModule(entryFile) : loadEffectSourceModule({
34
34
  resourcePath: entryFile,
35
35
  appDir: appDirectory || path.dirname(entryFile)
36
- });
36
+ }));
37
37
  } catch (error) {
38
38
  logger.error(`[BFF][Effect] Failed to load Effect entry: ${entryFile}\n${String(error)}`);
39
39
  this.handler = null;
@@ -3,27 +3,36 @@ import { BACKEND_FEDERATION_EFFECT_EXPOSE } from "@modern-js/utils/universal";
3
3
  import { formatBackendFederationIdentityIssues, validateExpectedBackendFederationIdentity } from "./identity.mjs";
4
4
  import { createBackendFederationRuntime } from "./runtime.mjs";
5
5
  import { isRecord, normalizeExpose } from "./utils.mjs";
6
+ import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
7
+ import * as __rspack_external_effect_Logger_1691a01b from "effect/Logger";
6
8
  const LEGACY_LOAD_WARNING = '[BFF][Effect] loadBackendFederatedEffectApi was called without an expected delivery-unit identity (expected.unitId + expected.buildMarker). Identity-less public backend loads are deprecated (ADR-0019/MV-G23) and will be rejected in a future major.';
7
- async function loadBackendFederatedEffectApi(options) {
9
+ const legacyWarningLogger = __rspack_external_effect_Logger_1691a01b.withLeveledConsole(__rspack_external_effect_Logger_1691a01b.formatLogFmt);
10
+ function warnLegacyLoad() {
11
+ __rspack_external_effect_Effect_41060786.runSync(__rspack_external_effect_Effect_41060786.provideService(__rspack_external_effect_Effect_41060786.logWarning(LEGACY_LOAD_WARNING), __rspack_external_effect_Logger_1691a01b.CurrentLoggers, new Set([
12
+ legacyWarningLogger
13
+ ])));
14
+ }
15
+ function loadBackendFederatedEffectApi(options) {
8
16
  const remoteName = options.remote?.name ?? options.remoteName;
9
- if (!remoteName) throw new Error('[BFF][Effect] Missing backend federation remote name.');
10
- if (void 0 === options.expected) console.warn(LEGACY_LOAD_WARNING);
17
+ if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
18
+ if (void 0 === options.expected) warnLegacyLoad();
11
19
  const runtime = options.runtime ?? createBackendFederationRuntime(options);
12
20
  const expose = options.expose ?? options.remote?.expose ?? BACKEND_FEDERATION_EFFECT_EXPOSE;
13
21
  const remoteRequest = `${remoteName}/${normalizeExpose(expose)}`;
14
- const loaded = await runtime.loadRemote(remoteRequest);
15
- if (!isRecord(loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must load an object module.`);
16
- const backendContract = loaded.backendFederationContract;
17
- if (!isRecord(backendContract) || true !== backendContract.strictEffectApproach) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (strictEffectApproach: true).`);
18
- if ('effect' !== backendContract.runtimeFramework) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (runtimeFramework: "effect").`);
19
- if ('string' == typeof backendContract.name && backendContract.name !== remoteName) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} metadata name mismatch: expected ${remoteName}, received ${backendContract.name}.`);
20
- if (!('runtime' in loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose api and runtime.`);
21
- if (void 0 !== options.expected) {
22
- const issues = validateExpectedBackendFederationIdentity(loaded, options.expected, {
23
- allowMissingIdentityMetadata: true === options.allowMissingIdentityMetadata
24
- });
25
- if (issues.length > 0) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} delivery-unit identity mismatch: ${formatBackendFederationIdentityIssues(issues)}.`);
26
- }
27
- return loaded;
22
+ return runtime.loadRemote(remoteRequest).then((loaded)=>{
23
+ if (!isRecord(loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must load an object module.`);
24
+ const backendContract = loaded.backendFederationContract;
25
+ if (!isRecord(backendContract) || true !== backendContract.strictEffectApproach) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (strictEffectApproach: true).`);
26
+ if ('effect' !== backendContract.runtimeFramework) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose strict Effect metadata (runtimeFramework: "effect").`);
27
+ if ('string' == typeof backendContract.name && backendContract.name !== remoteName) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} metadata name mismatch: expected ${remoteName}, received ${backendContract.name}.`);
28
+ if (!('runtime' in loaded)) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} must expose api and runtime.`);
29
+ if (void 0 !== options.expected) {
30
+ const issues = validateExpectedBackendFederationIdentity(loaded, options.expected, {
31
+ allowMissingIdentityMetadata: true === options.allowMissingIdentityMetadata
32
+ });
33
+ if (issues.length > 0) throw new Error(`[BFF][Effect] Backend federation expose ${remoteRequest} delivery-unit identity mismatch: ${formatBackendFederationIdentityIssues(issues)}.`);
34
+ }
35
+ return loaded;
36
+ });
28
37
  }
29
38
  export { loadBackendFederatedEffectApi };
@@ -2,13 +2,23 @@ import "node:module";
2
2
  function createBackendFederationLoadEntryPlugin(options) {
3
3
  return {
4
4
  name: 'modernjs-backend-federation-load-entry',
5
- async loadEntry ({ remoteInfo }) {
6
- return options.resolveEntry({
5
+ loadEntry ({ remoteInfo }) {
6
+ return Promise.resolve(options.resolveEntry({
7
7
  name: remoteInfo.name,
8
8
  entry: remoteInfo.entry,
9
9
  type: remoteInfo.type,
10
10
  entryGlobalName: remoteInfo.entryGlobalName,
11
11
  shareScope: remoteInfo.shareScope
12
+ })).then((entry)=>{
13
+ if (void 0 === entry) return;
14
+ return {
15
+ get (id) {
16
+ return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
17
+ },
18
+ init (...args) {
19
+ return entry.init?.(...args);
20
+ }
21
+ };
12
22
  });
13
23
  }
14
24
  };