@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,7 +1,6 @@
1
1
  import "node:module";
2
2
  import { deriveOperationVersion } from "@modern-js/bff-core";
3
- import { fs } from "@modern-js/utils";
4
- import path from "path";
3
+ import { fs, upath } from "@modern-js/utils";
5
4
  import { createEffectEndpointContractHash, ensureLeadingSlash, normalizeEffectPrefix } from "../../runtime/effect/endpoint-contracts.mjs";
6
5
  const DEFAULT_REQUEST_CREATOR = '@modern-js/plugin-bff/client';
7
6
  const EFFECT_CLIENT_RUNTIME_IMPORT = '@modern-js/plugin-bff/effect-client-runtime';
@@ -14,17 +13,17 @@ function isAbsoluteUrl(value) {
14
13
  }
15
14
  }
16
15
  function resolveBatchEndpoint(prefix, endpoint) {
17
- const value = endpoint || '/_data/batch';
16
+ const value = void 0 === endpoint || '' === endpoint ? '/_data/batch' : endpoint;
18
17
  if (isAbsoluteUrl(value)) return value;
19
18
  const normalizedPrefix = normalizeEffectPrefix(prefix);
20
19
  const normalizedEndpoint = ensureLeadingSlash(value);
21
- if (!normalizedPrefix) return normalizedEndpoint;
20
+ if ('' === normalizedPrefix) return normalizedEndpoint;
22
21
  if (normalizedEndpoint === normalizedPrefix || normalizedEndpoint.startsWith(`${normalizedPrefix}/`)) return normalizedEndpoint;
23
22
  return `${normalizedPrefix}${'/' === normalizedEndpoint ? '' : normalizedEndpoint}`;
24
23
  }
25
24
  function getPackageInfo(appDir) {
26
25
  try {
27
- const packageJsonPath = path.resolve(appDir, './package.json');
26
+ const packageJsonPath = upath.resolve(appDir, './package.json');
28
27
  const packageJson = fs.readJSONSync(packageJsonPath);
29
28
  return {
30
29
  name: packageJson.name,
@@ -39,9 +38,9 @@ function renderEffectClientCode(endpoints, options) {
39
38
  const httpMethodDecider = options.httpMethodDecider || 'functionName';
40
39
  const packageInfo = getPackageInfo(options.appDir);
41
40
  const packageName = packageInfo.name;
42
- const dataPlatformAppNamespace = packageName || 'unknown-app';
43
- const requestId = 'bundle' === options.target ? packageName || process.env.npm_package_name : void 0;
44
- const normalizedRequestId = requestId || 'default';
41
+ const dataPlatformAppNamespace = void 0 === packageName || '' === packageName ? 'unknown-app' : packageName;
42
+ const requestId = 'bundle' === options.target ? void 0 !== packageName && '' !== packageName ? packageName : void 0 : void 0;
43
+ const normalizedRequestId = void 0 === requestId || '' === requestId ? 'default' : requestId;
45
44
  const operationVersion = deriveOperationVersion(packageInfo.version);
46
45
  const batchConfig = options.dataPlatformBatch;
47
46
  const batchEndpoint = resolveBatchEndpoint(options.prefix, batchConfig?.endpoint);
@@ -58,7 +57,7 @@ function renderEffectClientCode(endpoints, options) {
58
57
  };
59
58
  const config = {
60
59
  appNamespace: dataPlatformAppNamespace,
61
- ...requestId ? {
60
+ ...void 0 !== requestId && '' !== requestId ? {
62
61
  requestId
63
62
  } : {},
64
63
  port: options.port,
@@ -72,7 +71,7 @@ function renderEffectClientCode(endpoints, options) {
72
71
  maxBatchSize: batchConfig?.maxBatchSize ?? 16,
73
72
  maxBatchBytes: batchConfig?.maxBatchBytes ?? 65536,
74
73
  requestTimeoutMs: batchConfig?.requestTimeoutMs ?? 10000,
75
- allowedMethods: batchConfig?.allowedMethods && batchConfig.allowedMethods.length > 0 ? batchConfig.allowedMethods : [
74
+ allowedMethods: batchConfig?.allowedMethods !== void 0 && batchConfig.allowedMethods.length > 0 ? batchConfig.allowedMethods : [
76
75
  'GET'
77
76
  ]
78
77
  }
@@ -1,36 +1,29 @@
1
1
  import __rslib_shim_module__ from "node:module";
2
2
  const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
3
- import { compatibleRequire } from "@modern-js/utils";
4
- import path from "path";
3
+ import { compatibleRequire, upath } from "@modern-js/utils";
5
4
  import { extractHttpApiFromModule } from "../../runtime/effect/endpoint-contracts.mjs";
6
5
  import { loadEffectSourceModule } from "../effectSourceLoader.mjs";
7
6
  let httpApiRuntimePromise;
8
7
  function isRecord(value) {
9
8
  return 'object' == typeof value && null !== value;
10
9
  }
11
- async function getHttpApiRuntime() {
12
- if (!httpApiRuntimePromise) httpApiRuntimePromise = (async ()=>{
13
- let mod;
14
- try {
15
- mod = await compatibleRequire('effect/unstable/httpapi', false);
16
- } catch (error) {
17
- const message = error instanceof Error ? error.message : String(error);
18
- if (!message.includes("Cannot find module 'effect/unstable/httpapi'")) throw error;
19
- const effectPackageJson = require.resolve('effect/package.json');
20
- const effectHttpApiRuntimePath = path.join(path.dirname(effectPackageJson), 'dist', 'unstable', 'httpapi', 'index.js');
21
- mod = await compatibleRequire(effectHttpApiRuntimePath, false);
22
- }
23
- if (isRecord(mod) && isRecord(mod.HttpApi)) {
24
- const maybeHttpApi = mod.HttpApi;
25
- if ('function' == typeof maybeHttpApi.isHttpApi && 'function' == typeof maybeHttpApi.reflect) return maybeHttpApi;
26
- }
10
+ function isHttpApiRuntime(value) {
11
+ return isRecord(value) && 'function' == typeof value.isHttpApi && 'function' == typeof value.reflect;
12
+ }
13
+ function getHttpApiRuntime() {
14
+ if (void 0 === httpApiRuntimePromise) httpApiRuntimePromise = compatibleRequire('effect/unstable/httpapi', false).catch((error)=>{
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ if (!message.includes("Cannot find module 'effect/unstable/httpapi'")) throw error;
17
+ const effectPackageJson = require.resolve('effect/package.json');
18
+ const effectHttpApiRuntimePath = upath.join(upath.dirname(effectPackageJson), 'dist', 'unstable', 'httpapi', 'index.js');
19
+ return compatibleRequire(effectHttpApiRuntimePath, false);
20
+ }).then((mod)=>{
21
+ if (isRecord(mod) && isHttpApiRuntime(mod.HttpApi)) return mod.HttpApi;
27
22
  throw new Error('[BFF][Effect] Unable to resolve HttpApi runtime from effect/unstable/httpapi.');
28
- })();
23
+ });
29
24
  return httpApiRuntimePromise;
30
25
  }
31
- async function loadEffectApi(options) {
32
- const httpApiRuntime = await getHttpApiRuntime();
33
- const mod = await loadEffectSourceModule(options);
34
- return extractHttpApiFromModule(mod, httpApiRuntime.isHttpApi);
26
+ function loadEffectApi(options) {
27
+ return getHttpApiRuntime().then((httpApiRuntime)=>loadEffectSourceModule(options).then((mod)=>extractHttpApiFromModule(mod, httpApiRuntime.isHttpApi)));
35
28
  }
36
29
  export { getHttpApiRuntime, loadEffectApi };
@@ -1,7 +1,7 @@
1
1
  import "node:module";
2
2
  import node_crypto from "node:crypto";
3
3
  import node_fs from "node:fs";
4
- import { builtinModules } from "node:module";
4
+ import { builtinModules, createRequire } from "node:module";
5
5
  import node_path from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import { build, transform } from "esbuild";
@@ -76,10 +76,29 @@ const nodeBuiltins = new Set([
76
76
  ...builtinModules.map((name)=>`node:${name}`)
77
77
  ]);
78
78
  const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
79
+ async function isEsmOnlyFile(filename) {
80
+ const extension = node_path.extname(filename).toLowerCase();
81
+ if ('.mjs' === extension || '.mts' === extension) return true;
82
+ if ('.cjs' === extension || '.cts' === extension) return false;
83
+ let directory = node_path.dirname(filename);
84
+ while(true){
85
+ const packagePath = node_path.join(directory, 'package.json');
86
+ try {
87
+ const packageJson = JSON.parse(await node_fs.promises.readFile(packagePath, 'utf8'));
88
+ return 'module' === packageJson.type;
89
+ } catch (error) {
90
+ if (!error || 'object' != typeof error || !('code' in error) || 'ENOENT' !== error.code) throw error;
91
+ }
92
+ const parent = node_path.dirname(directory);
93
+ if (parent === directory) return false;
94
+ directory = parent;
95
+ }
96
+ }
79
97
  function isBareSpecifier(specifier) {
80
98
  return !specifier.startsWith('.') && !specifier.startsWith('/') && !node_path.isAbsolute(specifier);
81
99
  }
82
- function externalizeInstalledDependencies() {
100
+ function externalizeInstalledDependencies(options) {
101
+ const runtimeRequire = createRequire(node_path.join(options.runtimeResolveDir, '__modern_effect_entry.cjs'));
83
102
  return {
84
103
  name: 'modern-js-effect-installed-dependencies',
85
104
  setup (buildApi) {
@@ -109,19 +128,46 @@ function externalizeInstalledDependencies() {
109
128
  path: args.path
110
129
  };
111
130
  const realPath = await node_fs.promises.realpath(resolution.path).catch(()=>resolution.path);
112
- if (realPath.includes(`${node_path.sep}node_modules${node_path.sep}`)) return {
113
- external: true,
114
- path: args.path
115
- };
131
+ if (realPath.includes(`${node_path.sep}node_modules${node_path.sep}`)) {
132
+ const runtimeResolution = await buildApi.resolve(args.path, {
133
+ importer: '',
134
+ kind: args.kind,
135
+ namespace: 'file',
136
+ pluginData: {
137
+ [dependencyResolutionMarker]: true
138
+ },
139
+ resolveDir: options.runtimeResolveDir
140
+ });
141
+ if (0 === runtimeResolution.errors.length && runtimeResolution.path) {
142
+ if ('cjs' === options.format) {
143
+ let requiredPath;
144
+ try {
145
+ requiredPath = runtimeRequire.resolve(args.path);
146
+ } catch {
147
+ requiredPath = runtimeResolution.path;
148
+ }
149
+ if (await isEsmOnlyFile(requiredPath)) return {
150
+ namespace: resolution.namespace,
151
+ path: resolution.path,
152
+ pluginData: resolution.pluginData,
153
+ sideEffects: resolution.sideEffects,
154
+ suffix: resolution.suffix,
155
+ warnings: resolution.warnings
156
+ };
157
+ }
158
+ return {
159
+ external: true,
160
+ path: args.path
161
+ };
162
+ }
163
+ }
116
164
  return {
117
165
  namespace: resolution.namespace,
118
166
  path: resolution.path,
119
167
  pluginData: resolution.pluginData,
120
168
  sideEffects: resolution.sideEffects,
121
169
  suffix: resolution.suffix,
122
- warnings: resolution.warnings,
123
- watchDirs: resolution.watchDirs,
124
- watchFiles: resolution.watchFiles
170
+ warnings: resolution.warnings
125
171
  };
126
172
  });
127
173
  }
@@ -140,7 +186,10 @@ async function bundleEffectEntryForNode(options) {
140
186
  outfile: options.entryPath,
141
187
  platform: 'node',
142
188
  plugins: [
143
- externalizeInstalledDependencies(),
189
+ externalizeInstalledDependencies({
190
+ format: options.format,
191
+ runtimeResolveDir: options.appDir
192
+ }),
144
193
  preserveSourceModuleSemantics()
145
194
  ],
146
195
  target: 'node20'
@@ -156,6 +205,11 @@ function normalizeImportedModule(module) {
156
205
  if (1 === Object.keys(namespace).length && null !== commonJsModule && 'object' == typeof commonJsModule && true === commonJsModule.__esModule) return commonJsModule;
157
206
  return module;
158
207
  }
208
+ async function loadEffectBuiltModule(resourcePath) {
209
+ const canonicalPath = await node_fs.promises.realpath(node_path.resolve(resourcePath));
210
+ if (!await isEsmOnlyFile(canonicalPath)) return createRequire(canonicalPath)(canonicalPath);
211
+ return normalizeImportedModule(await dynamicImport(pathToFileURL(canonicalPath).href));
212
+ }
159
213
  async function loadEffectSourceModule(options) {
160
214
  const resourcePath = node_path.resolve(options.resourcePath);
161
215
  const appDir = node_path.resolve(options.appDir ?? node_path.dirname(resourcePath));
@@ -182,7 +236,10 @@ async function loadEffectSourceModule(options) {
182
236
  outfile: outputPath,
183
237
  platform: 'node',
184
238
  plugins: [
185
- externalizeInstalledDependencies(),
239
+ externalizeInstalledDependencies({
240
+ format: 'esm',
241
+ runtimeResolveDir: appDir
242
+ }),
186
243
  preserveSourceModuleSemantics()
187
244
  ],
188
245
  sourcemap: 'inline',
@@ -197,4 +254,4 @@ async function loadEffectSourceModule(options) {
197
254
  const revision = node_crypto.createHash('sha256').update(output).digest('hex');
198
255
  return normalizeImportedModule(await dynamicImport(`${pathToFileURL(outputPath).href}?v=${revision}`));
199
256
  }
200
- export { bundleEffectEntryForNode, loadEffectSourceModule };
257
+ export { bundleEffectEntryForNode, loadEffectBuiltModule, loadEffectSourceModule };
@@ -1,22 +1,22 @@
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, LAMBDA_DIR, PACKAGE_NAME, PREFIX, RUNTIME_FRAMEWORK } from "./crossProjectApiPlugin.mjs";
4
+ import { API_DIR, DIST_DIR, EFFECT_ENTRY, LAMBDA_DIR, OPERATION_CONTRACTS_JSON, PACKAGE_NAME, PREFIX, RUNTIME_FRAMEWORK } from "./crossProjectApiPlugin.mjs";
5
5
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
6
6
  import { dirname as __rspack_dirname } from "node:path";
7
7
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
8
- function replaceContent(source, packageName, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework) {
9
- const updatedSource = source.replace(new RegExp(PACKAGE_NAME, 'g'), packageName).replace(new RegExp(PREFIX, 'g'), prefix).replace(new RegExp(DIST_DIR, 'g'), normalizeToPosixPath(relativeDistPath)).replace(new RegExp(API_DIR, 'g'), normalizeToPosixPath(relativeApiPath)).replace(new RegExp(LAMBDA_DIR, 'g'), normalizeToPosixPath(relativeLambdaPath)).replace(new RegExp(RUNTIME_FRAMEWORK, 'g'), runtimeFramework);
8
+ function replaceContent(source, packageName, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts) {
9
+ const updatedSource = source.replace(new RegExp(PACKAGE_NAME, 'g'), packageName).replace(new RegExp(PREFIX, 'g'), prefix).replace(new RegExp(DIST_DIR, 'g'), normalizeToPosixPath(relativeDistPath)).replace(new RegExp(API_DIR, 'g'), normalizeToPosixPath(relativeApiPath)).replace(new RegExp(LAMBDA_DIR, 'g'), normalizeToPosixPath(relativeLambdaPath)).replace(new RegExp(RUNTIME_FRAMEWORK, 'g'), runtimeFramework).replace(new RegExp(EFFECT_ENTRY, 'g'), normalizeToPosixPath(relativeEffectEntry)).replace(`'${OPERATION_CONTRACTS_JSON}'`, ()=>JSON.stringify(JSON.stringify(operationContracts)));
10
10
  return updatedSource;
11
11
  }
12
- async function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework }) {
12
+ async function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts }) {
13
13
  try {
14
14
  const packageContent = await fs.readFile(path.resolve(appDirectory, './package.json'), 'utf8');
15
15
  const packageJson = JSON.parse(packageContent);
16
16
  const pluginDir = path.resolve(appDirectory, `./${relativeDistPath}`, 'plugin');
17
17
  const pluginPath = path.join(pluginDir, 'index.js');
18
18
  const pluginTemplate = await fs.readFile(path.resolve(__rspack_import_meta_dirname__, 'crossProjectApiPlugin.js'), 'utf8');
19
- const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework);
19
+ const updatedPlugin = replaceContent(pluginTemplate, packageJson.name, prefix, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts);
20
20
  await fs.ensureFile(pluginPath);
21
21
  await fs.writeFile(pluginPath, updatedPlugin);
22
22
  const typeContent = `import type { AppTools, CliPlugin } from '@modern-js/app-tools';
@@ -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>;
4
+ compileApi: () => Promise<void | undefined>;
5
5
  generate: () => Promise<void>;
6
- handleCrossProjectInvocation: (isBuild?: boolean) => Promise<void>;
6
+ handleCrossProjectInvocation: (isBuild?: boolean) => Promise<void | undefined>;
7
7
  };
@@ -1,4 +1,6 @@
1
+ import type { AppTools } from '@modern-js/app-tools';
1
2
  type SerializedGlobalVars = Record<string, string>;
2
- export declare const serializeServerGlobalVars: (options: unknown) => SerializedGlobalVars;
3
- export declare const transformServerGlobalVars: (outputDirectories: string[], globalVars: SerializedGlobalVars) => Promise<void>;
3
+ type ServerGlobalVarsOptions = NonNullable<NonNullable<AppTools['config']>['source']>['globalVars'];
4
+ export declare const serializeServerGlobalVars: (options: ServerGlobalVarsOptions) => SerializedGlobalVars;
5
+ export declare const transformServerGlobalVars: (outputDirectories: string[], globalVars: SerializedGlobalVars) => Promise<undefined>;
4
6
  export {};
@@ -1,2 +1,3 @@
1
1
  import type { AppTools, CliPlugin } from '@modern-js/app-tools';
2
2
  export declare const bffPlugin: () => CliPlugin<AppTools>;
3
+ export default bffPlugin;
@@ -1,4 +1,4 @@
1
1
  import type { ServerPluginAPI } from '@modern-js/server-core';
2
2
  import { type ResolvedCrossProjectPolicy } from '../../../utils/crossProjectServerPolicy';
3
3
  import type { EffectApiModule } from '../module';
4
- export declare function resolveEffectAdapterCrossProjectPolicy(api: ServerPluginAPI, prefix: string, mod: EffectApiModule | null): Promise<ResolvedCrossProjectPolicy>;
4
+ export declare function resolveEffectAdapterCrossProjectPolicy(api: ServerPluginAPI, prefix: string, mod: EffectApiModule | null): Promise<ResolvedCrossProjectPolicy | undefined>;
@@ -1,3 +1,3 @@
1
- import type { ModuleFederation } from '@module-federation/runtime';
1
+ import { ModuleFederation } from '@module-federation/runtime';
2
2
  import type { BackendFederationRuntimeOptions } from './types';
3
3
  export declare function createBackendFederationRuntime(options: BackendFederationRuntimeOptions): ModuleFederation;
@@ -0,0 +1,26 @@
1
+ import { type DispatchEffectBffRequestOptions } from './dispatch';
2
+ import type { EffectBffOpenApiConfig, EffectDataPlatformValidationOptions } from './handler';
3
+ import { type EffectApiModule, type EffectBffRequestHandler } from './module';
4
+ import type { EffectContext } from './operation-context';
5
+ export { runWithEffectContext, useEffectContext, useOperationContext, } from './edge-context';
6
+ export * from './handler';
7
+ export { type CreateEffectOperationContextOptions, createEffectOperationContext, type EffectContext, } from './operation-context';
8
+ export type EffectBffEdgeDispatchOptions = Omit<DispatchEffectBffRequestOptions, 'runWithEffectContext'>;
9
+ export type EffectBffEdgeHandlerOptions = {
10
+ module: EffectApiModule;
11
+ prefix?: string;
12
+ openapi?: EffectBffOpenApiConfig;
13
+ dataPlatform?: EffectDataPlatformValidationOptions;
14
+ onError?: (error: unknown, context: EffectContext) => Promise<Response> | Response;
15
+ onWarning?: (message: string) => void;
16
+ };
17
+ export declare function dispatchEffectBffRequest(handler: EffectBffRequestHandler, request: Request, options?: EffectBffEdgeDispatchOptions): Promise<Response>;
18
+ export declare function createEffectBffEdgeDispatcher(options: EffectBffEdgeHandlerOptions): Promise<{
19
+ dispatch: (request: Request, dispatchOptions?: Omit<EffectBffEdgeDispatchOptions, 'prefix' | 'onError'>) => Promise<Response>;
20
+ dispose: () => Promise<void>;
21
+ }>;
22
+ export declare const createEffectBffTestHandler: typeof createEffectBffEdgeHandler;
23
+ export declare function createEffectBffEdgeHandler(options: EffectBffEdgeHandlerOptions): Promise<{
24
+ handler: (request: Request, dispatchOptions?: Omit<EffectBffEdgeDispatchOptions, 'prefix' | 'onError'>) => Promise<Response>;
25
+ dispose: () => Promise<void>;
26
+ }>;
@@ -1,27 +1,2 @@
1
- import { type DispatchEffectBffRequestOptions } from './dispatch';
2
- import type { EffectBffOpenApiConfig, EffectDataPlatformValidationOptions } from './handler';
3
- import { type EffectApiModule, type EffectBffRequestHandler } from './module';
4
- import type { EffectContext } from './operation-context';
5
1
  export { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, type BackendFederatedEffectApiModule, type BackendFederationEntryExports, type BackendFederationExpectedIdentity, type BackendFederationIdentityIssue, type BackendFederationIdentityLoadOptions, type BackendFederationLoadEntryPluginOptions, type BackendFederationRemote, type BackendFederationRuntimeOptions, createBackendFederationLoadEntryPlugin, createBackendFederationRuntime, loadBackendFederatedEffectApi, validateExpectedBackendFederationIdentity, } from './backend-federation';
6
- export { runWithEffectContext, useEffectContext, useOperationContext, } from './edge-context';
7
- export * from './handler';
8
- export { type CreateEffectOperationContextOptions, createEffectOperationContext, type EffectContext, } from './operation-context';
9
- export type EffectBffEdgeDispatchOptions = Omit<DispatchEffectBffRequestOptions, 'runWithEffectContext'>;
10
- export type EffectBffEdgeHandlerOptions = {
11
- module: EffectApiModule;
12
- prefix?: string;
13
- openapi?: EffectBffOpenApiConfig;
14
- dataPlatform?: EffectDataPlatformValidationOptions;
15
- onError?: (error: unknown, context: EffectContext) => Promise<Response> | Response;
16
- onWarning?: (message: string) => void;
17
- };
18
- export declare function dispatchEffectBffRequest(handler: EffectBffRequestHandler, request: Request, options?: EffectBffEdgeDispatchOptions): Promise<Response>;
19
- export declare function createEffectBffEdgeDispatcher(options: EffectBffEdgeHandlerOptions): Promise<{
20
- dispatch: (request: Request, dispatchOptions?: Omit<EffectBffEdgeDispatchOptions, 'prefix' | 'onError'>) => Promise<Response>;
21
- dispose: () => Promise<void>;
22
- }>;
23
- export declare const createEffectBffTestHandler: typeof createEffectBffEdgeHandler;
24
- export declare function createEffectBffEdgeHandler(options: EffectBffEdgeHandlerOptions): Promise<{
25
- handler: (request: Request, dispatchOptions?: Omit<EffectBffEdgeDispatchOptions, 'prefix' | 'onError'>) => Promise<Response>;
26
- dispose: () => Promise<void>;
27
- }>;
2
+ export * from './edge-dispatcher';
@@ -25,6 +25,12 @@ type HttpApiGroupLike = {
25
25
  identifier?: unknown;
26
26
  };
27
27
  type HttpApiEndpointLike = {
28
+ /**
29
+ * effect 4.0.0-beta.98 renamed the endpoint `name` property to `identifier`,
30
+ * matching `HttpApiGroup`. `name` is kept as a fallback so reflection over an
31
+ * older Effect build does not silently degrade to the class name.
32
+ */
33
+ identifier?: unknown;
28
34
  name?: unknown;
29
35
  method?: unknown;
30
36
  path?: unknown;
@@ -1,17 +1,7 @@
1
1
  import type { HttpApi } from 'effect/unstable/httpapi';
2
2
  import type { Rpc } from 'effect/unstable/rpc';
3
- import type { EffectBffRuntime, EffectDataPlatformValidationOptions, EffectRpcBffDefinition, EffectRpcBffHandlerFactory, EffectRpcRuntimeLayer, EffectRuntimeLayer } from './types';
4
- export declare function defineEffectBff<TApi extends HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer, TRpcs extends Rpc.Any = Rpc.Any>(definition: {
5
- api: TApi;
6
- layer: TLayer;
7
- rpc?: EffectRpcBffDefinition<TRpcs>;
8
- dataPlatform?: EffectDataPlatformValidationOptions;
9
- }): {
10
- api: TApi;
11
- layer: TLayer;
12
- rpc?: EffectRpcBffDefinition<TRpcs>;
13
- dataPlatform?: EffectDataPlatformValidationOptions;
14
- } & EffectBffRuntime<TApi, TLayer>;
3
+ import type { EffectBffDefinition, EffectBffRuntime, EffectRpcBffDefinition, EffectRpcBffHandlerFactory, EffectRpcRuntimeLayer, EffectRuntimeLayer } from './types';
4
+ export declare function defineEffectBff<TApi extends HttpApi.Constraint, TLayer extends EffectRuntimeLayer, TRpcs extends Rpc.Any = Rpc.Any>(definition: EffectBffDefinition<TApi, TLayer, TRpcs>): EffectBffDefinition<TApi, TLayer, TRpcs> & EffectBffRuntime<TApi, TLayer>;
15
5
  export declare function defineEffectRpcBff<TRpcs extends Rpc.Any = Rpc.Any, TLayer extends EffectRpcRuntimeLayer<TRpcs> = EffectRpcRuntimeLayer<TRpcs>>(definition: EffectRpcBffDefinition<TRpcs, TLayer>): EffectRpcBffDefinition<TRpcs, TLayer> & {
16
6
  createHandler: EffectRpcBffHandlerFactory<TRpcs>;
17
7
  };
@@ -1,12 +1,13 @@
1
1
  import type { HttpApi } from 'effect/unstable/httpapi';
2
2
  import type { Rpc } from 'effect/unstable/rpc';
3
- import type { EffectBffOpenApiConfig, EffectDataPlatformValidationOptions, EffectRequestValidator, EffectRpcBffDefinition, EffectRuntimeLayer } from './types';
4
- export declare function createHttpApiHandler<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TRpcs extends Rpc.Any = Rpc.Any>(options: {
3
+ import type { EffectBffOpenApiConfig, EffectDataPlatformValidationOptions, EffectRequestInterceptor, EffectRequestValidator, EffectRpcBffDefinition, EffectRuntimeLayer } from './types';
4
+ export declare function createHttpApiHandler<TApi extends HttpApi.Constraint = HttpApi.Top, TRpcs extends Rpc.Any = Rpc.Any>(options: {
5
5
  api: TApi;
6
6
  layer: EffectRuntimeLayer;
7
7
  openapi?: EffectBffOpenApiConfig;
8
8
  rpc?: EffectRpcBffDefinition<TRpcs>;
9
9
  dataPlatform?: EffectDataPlatformValidationOptions;
10
+ interceptRequest?: EffectRequestInterceptor;
10
11
  validateRequest?: EffectRequestValidator;
11
12
  }): {
12
13
  handler: (request: Request, context?: Parameters<(request: globalThis.Request, context: import("effect/Context").Context<unknown>) => Promise<Response>>[1]) => Promise<Response>;
@@ -1,4 +1,4 @@
1
1
  import { HttpRouter } from 'effect/unstable/http';
2
2
  import { type HttpApi } from 'effect/unstable/httpapi';
3
3
  import type { EffectBffOpenApiConfig } from './types';
4
- export declare function createOpenApiLayer(api: HttpApi.AnyWithProps, openapi: EffectBffOpenApiConfig | undefined): import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter> | null;
4
+ export declare function createOpenApiLayer(api: HttpApi.Constraint, openapi: EffectBffOpenApiConfig | undefined): import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter> | null;
@@ -28,13 +28,14 @@ type EffectApiPromiseClient<TClient> = {
28
28
  [EndpointName in keyof TClient[GroupName]]: TClient[GroupName][EndpointName] extends (...args: infer TArgs) => infer TResult ? TResult extends EffectType.Effect<unknown, unknown, unknown> ? (...args: TArgs) => Promise<Exclude<EffectType.Success<TResult>, readonly [unknown, unknown]>> : never : never;
29
29
  };
30
30
  };
31
- export type EffectApiClientFromApi<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps> = TApi extends HttpApi.HttpApi<infer _ApiId, infer Groups> ? HttpApiClient.Client<Extract<Groups, HttpApiGroup.Any>, never, never> : never;
32
- export type EffectApiPromiseClientFromApi<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps> = EffectApiPromiseClient<EffectApiClientFromApi<TApi>>;
33
- export type EffectBffDefinition<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer, TRpcs extends Rpc.Any = Rpc.Any> = {
31
+ export type EffectApiClientFromApi<TApi extends HttpApi.Constraint = HttpApi.Top> = TApi extends HttpApi.HttpApi<infer _ApiId, infer Groups> ? HttpApiClient.Client<Extract<Groups, HttpApiGroup.Constraint>, never, never> : never;
32
+ export type EffectApiPromiseClientFromApi<TApi extends HttpApi.Constraint = HttpApi.Top> = EffectApiPromiseClient<EffectApiClientFromApi<TApi>>;
33
+ export type EffectBffDefinition<TApi extends HttpApi.Constraint = HttpApi.Top, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer, TRpcs extends Rpc.Any = Rpc.Any> = {
34
34
  api: TApi;
35
35
  layer: TLayer;
36
36
  rpc?: EffectRpcBffDefinition<TRpcs>;
37
37
  dataPlatform?: EffectDataPlatformValidationOptions;
38
+ interceptRequest?: EffectRequestInterceptor;
38
39
  };
39
40
  export type EffectDataPlatformSelectionValidationOptions = {
40
41
  maxDepth?: number;
@@ -131,13 +132,17 @@ export type EffectDataPlatformValidationOptions = {
131
132
  * cannot bypass it.
132
133
  */
133
134
  export type EffectRequestValidator = (request: Request) => Response | null | undefined;
134
- export type EffectBffHandlerFactory<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = (options?: {
135
+ export type EffectRequestInterceptor = (options: {
136
+ request: Request;
137
+ next: () => Promise<Response>;
138
+ }) => Response | Promise<Response>;
139
+ export type EffectBffHandlerFactory<TApi extends HttpApi.Constraint = HttpApi.Top, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = (options?: {
135
140
  openapi?: EffectBffOpenApiConfig;
136
141
  rpc?: Partial<EffectRpcBffHandlerOptions>;
137
142
  dataPlatform?: Partial<EffectDataPlatformValidationOptions>;
138
143
  validateRequest?: EffectRequestValidator;
139
144
  }) => ReturnType<typeof createHttpApiHandler>;
140
- export type EffectBffRuntime<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = {
145
+ export type EffectBffRuntime<TApi extends HttpApi.Constraint = HttpApi.Top, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = {
141
146
  createHandler: EffectBffHandlerFactory<TApi, TLayer>;
142
147
  client: EffectApiPromiseClientFromApi<TApi>;
143
148
  };
@@ -53,6 +53,6 @@ export type EffectRpcClientHandle<Rpcs extends Rpc.Any, Flatten extends boolean
53
53
  export declare function view<T>(): <const Selection extends EffectViewSelection<T>>(selection: Selection) => Selection;
54
54
  export declare function mask<T, const Selection extends EffectViewSelection<T>>(value: T, selection: Selection): EffectViewData<T, Selection>;
55
55
  export declare function runEffectView<T, const Selection extends EffectViewSelection<T>>(request: PromiseLike<T>, selection: Selection): Promise<EffectViewData<T, Selection>>;
56
- export declare function makeEffectHttpApiClient<ApiId extends string, Groups extends HttpApiGroup.Any>(api: HttpApi.HttpApi<ApiId, Groups>, options?: EffectHttpApiClientOptions): Effect.Effect<HttpApiClient.Client<Groups, never, never>, never, HttpApiGroup.MiddlewareClient<Groups>>;
56
+ export declare function makeEffectHttpApiClient<ApiId extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<ApiId, Groups>, options?: EffectHttpApiClientOptions): Effect.Effect<HttpApiClient.Client<Groups, never, never>, never, HttpApiGroup.MiddlewareClient<Groups>>;
57
57
  export declare function makeEffectRpcClient<Rpcs extends Rpc.Any, const Flatten extends boolean = false>(group: RpcGroup.RpcGroup<Rpcs>, options: EffectRpcClientOptions<Rpcs, Flatten>): Effect.Effect<EffectRpcClientHandle<Rpcs, Flatten>, EffectRpcClientError, never>;
58
58
  export declare const runEffectRequest: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>;
@@ -1,22 +1,28 @@
1
- import type { MiddlewareHandler, ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
2
- import { Hono } from '@modern-js/server-core';
1
+ import type { ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
3
2
  import { type ResolvedCrossProjectPolicy } from '../../utils/crossProjectServerPolicy';
4
3
  interface MiddlewareOptions {
5
- prefix: string;
4
+ prefix?: string;
6
5
  enableHandleWeb?: boolean;
7
6
  }
8
7
  export declare class HonoAdapter {
9
8
  apiMiddleware: ServerMiddleware[];
10
- apiServer: Hono | null;
11
9
  api: ServerPluginAPI;
12
10
  isHono: boolean;
13
11
  prefix: string;
14
12
  crossProjectPolicy: ResolvedCrossProjectPolicy | undefined;
15
13
  constructor(api: ServerPluginAPI);
16
14
  setHandlers: () => Promise<void>;
17
- registerApiRoutes: () => Promise<void>;
18
- registerMiddleware: (options: MiddlewareOptions) => Promise<void>;
19
- onApiHandlersUpdated: () => Promise<void>;
20
- wrapInArray(handler: MiddlewareHandler[] | MiddlewareHandler): MiddlewareHandler[];
15
+ /**
16
+ * Register the BFF API routes as ordinary server middlewares.
17
+ *
18
+ * Dev and prod now share this single path: the routes are pushed straight
19
+ * into the server's middleware list. In dev, hot updates are handled by the
20
+ * unified `@modern-js/server` runtime reload (which rebuilds the whole
21
+ * runtime — including this plugin — from scratch), so BFF no longer needs its
22
+ * own swappable Hono sub-app / dynamic dispatch middleware.
23
+ */
24
+ registerMiddleware: (options?: MiddlewareOptions) => Promise<void>;
25
+ private handleRouteError;
26
+ private withErrorBoundary;
21
27
  }
22
28
  export {};
@@ -5,5 +5,7 @@ export declare const API_DIR = "{apiDirectory}";
5
5
  export declare const LAMBDA_DIR = "{lambdaDirectory}";
6
6
  export declare const DIST_DIR = "{distDirectory}";
7
7
  export declare const RUNTIME_FRAMEWORK: string;
8
+ export declare const EFFECT_ENTRY = "{effectEntry}";
9
+ export declare const OPERATION_CONTRACTS_JSON = "{operationContracts}";
8
10
  export declare const crossProjectApiPlugin: () => CliPlugin<AppTools>;
9
11
  export default crossProjectApiPlugin;
@@ -8,6 +8,16 @@ export declare function bundleEffectEntryForNode(options: {
8
8
  entryPath: string;
9
9
  format: 'cjs' | 'esm';
10
10
  }): Promise<void>;
11
+ /**
12
+ * Loads the relocatable artifact emitted by the production BFF build.
13
+ *
14
+ * Production entries have already been compiled and bundled by
15
+ * `bundleEffectEntryForNode`. Loading that artifact through the development
16
+ * source-graph bundler a second time changes its module format and can strip
17
+ * CommonJS runtime globals from bundled dependencies. Keep the built module's
18
+ * native Node boundary intact instead.
19
+ */
20
+ export declare function loadEffectBuiltModule(resourcePath: string): Promise<unknown>;
11
21
  /**
12
22
  * Loads an Effect source entry as native ESM while keeping its complete local
13
23
  * source graph observable by the framework watcher.
@@ -1,9 +1,11 @@
1
- declare function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, }: {
1
+ declare function pluginGenerator({ prefix, appDirectory, relativeDistPath, relativeApiPath, relativeLambdaPath, runtimeFramework, relativeEffectEntry, operationContracts, }: {
2
2
  prefix: string;
3
3
  appDirectory: string;
4
4
  relativeDistPath: string;
5
5
  relativeApiPath: string;
6
6
  relativeLambdaPath: string;
7
7
  runtimeFramework: 'hono' | 'effect';
8
+ relativeEffectEntry: string;
9
+ operationContracts: Record<string, unknown>;
8
10
  }): Promise<void>;
9
11
  export default pluginGenerator;