@bleedingdev/modern-js-app-tools 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/cjs/builder/generator/index.js +0 -1
  2. package/dist/cjs/builder/shared/builderPlugins/index.js +4 -11
  3. package/dist/cjs/config/index.js +4 -11
  4. package/dist/cjs/index.js +80 -111
  5. package/dist/cjs/plugins/deploy/platforms/node.js +0 -13
  6. package/dist/cjs/plugins/serverBuild.js +2 -1
  7. package/dist/esm/builder/generator/index.mjs +1 -2
  8. package/dist/esm/builder/shared/builderPlugins/index.mjs +0 -1
  9. package/dist/esm/config/index.mjs +0 -1
  10. package/dist/esm/index.mjs +1 -6
  11. package/dist/esm/plugins/deploy/platforms/node.mjs +0 -13
  12. package/dist/esm/plugins/serverBuild.mjs +2 -1
  13. package/dist/esm-node/builder/generator/index.mjs +1 -2
  14. package/dist/esm-node/builder/shared/builderPlugins/index.mjs +0 -1
  15. package/dist/esm-node/config/index.mjs +0 -1
  16. package/dist/esm-node/index.mjs +1 -6
  17. package/dist/esm-node/plugins/deploy/platforms/node.mjs +0 -13
  18. package/dist/esm-node/plugins/serverBuild.mjs +2 -1
  19. package/dist/types/builder/shared/builderPlugins/index.d.ts +0 -1
  20. package/dist/types/config/index.d.ts +0 -1
  21. package/dist/types/index.d.ts +0 -1
  22. package/dist/types/plugins/deploy/utils/generator.d.ts +1 -1
  23. package/dist/types/types/config/output.d.ts +0 -9
  24. package/dist/types/types/index.d.ts +1 -1
  25. package/dist/types/types/plugin.d.ts +56 -2
  26. package/dist/types/utils/initAppContext.d.ts +3 -2
  27. package/docs/configure/app/bff/effect.md +29 -24
  28. package/docs/guides/advanced-features/bff/data-platform.md +2 -2
  29. package/docs/guides/advanced-features/bff/frameworks.md +36 -24
  30. package/docs/guides/advanced-features/bff/function.md +2 -2
  31. package/docs/guides/advanced-features/bff/operators.md +17 -17
  32. package/docs/guides/get-started/quick-start.md +1 -5
  33. package/docs/guides/get-started/ultramodern.md +5 -86
  34. package/docs/llms.txt +2 -2
  35. package/package.json +14 -30
  36. package/rslib.config.mts +0 -5
  37. package/dist/cjs/builder/shared/builderPlugins/adapterPrecompress.js +0 -125
  38. package/dist/cjs/config/build-environment.js +0 -268
  39. package/dist/cjs/config/public.js +0 -46
  40. package/dist/cjs/plugins/backend-federation/codegen.js +0 -256
  41. package/dist/cjs/plugins/backend-federation/config.js +0 -204
  42. package/dist/cjs/plugins/backend-federation/emit.js +0 -150
  43. package/dist/cjs/plugins/backendFederationBuild.js +0 -53
  44. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js +0 -62
  45. package/dist/cjs/plugins/deploy/utils/npmAliases.js +0 -226
  46. package/dist/cjs/plugins/ultramodernReleaseEnvelope.js +0 -44
  47. package/dist/cjs/presetUltramodern.js +0 -175
  48. package/dist/cjs/types/config/precompress.js +0 -18
  49. package/dist/esm/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -77
  50. package/dist/esm/config/build-environment.mjs +0 -224
  51. package/dist/esm/config/public.mjs +0 -1
  52. package/dist/esm/plugins/backend-federation/codegen.mjs +0 -205
  53. package/dist/esm/plugins/backend-federation/config.mjs +0 -128
  54. package/dist/esm/plugins/backend-federation/emit.mjs +0 -99
  55. package/dist/esm/plugins/backendFederationBuild.mjs +0 -1
  56. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -1
  57. package/dist/esm/plugins/deploy/utils/npmAliases.mjs +0 -174
  58. package/dist/esm/plugins/ultramodernReleaseEnvelope.mjs +0 -6
  59. package/dist/esm/presetUltramodern.mjs +0 -119
  60. package/dist/esm/types/config/precompress.mjs +0 -0
  61. package/dist/esm-node/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -78
  62. package/dist/esm-node/config/build-environment.mjs +0 -225
  63. package/dist/esm-node/config/public.mjs +0 -2
  64. package/dist/esm-node/plugins/backend-federation/codegen.mjs +0 -206
  65. package/dist/esm-node/plugins/backend-federation/config.mjs +0 -129
  66. package/dist/esm-node/plugins/backend-federation/emit.mjs +0 -100
  67. package/dist/esm-node/plugins/backendFederationBuild.mjs +0 -2
  68. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -2
  69. package/dist/esm-node/plugins/deploy/utils/npmAliases.mjs +0 -175
  70. package/dist/esm-node/plugins/ultramodernReleaseEnvelope.mjs +0 -7
  71. package/dist/esm-node/presetUltramodern.mjs +0 -120
  72. package/dist/esm-node/types/config/precompress.mjs +0 -1
  73. package/dist/types/builder/shared/builderPlugins/adapterPrecompress.d.ts +0 -3
  74. package/dist/types/config/build-environment.d.ts +0 -22
  75. package/dist/types/config/public.d.ts +0 -2
  76. package/dist/types/plugins/backend-federation/codegen.d.ts +0 -86
  77. package/dist/types/plugins/backend-federation/config.d.ts +0 -103
  78. package/dist/types/plugins/backend-federation/emit.d.ts +0 -13
  79. package/dist/types/plugins/backendFederationBuild.d.ts +0 -2
  80. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/index.d.ts +0 -1
  81. package/dist/types/plugins/deploy/utils/npmAliases.d.ts +0 -14
  82. package/dist/types/plugins/ultramodernReleaseEnvelope.d.ts +0 -3
  83. package/dist/types/precompress/LICENSE +0 -20
  84. package/dist/types/precompress/index.d.ts +0 -76
  85. package/dist/types/presetUltramodern.d.ts +0 -87
  86. package/dist/types/types/config/precompress.d.ts +0 -19
@@ -5,7 +5,6 @@ import { chalk, fs, removeModuleSyncFromExports } from "@modern-js/utils";
5
5
  import { nodeDepEmit } from "ndepe";
6
6
  import { readTemplate, resolveESMDependency } from "../utils/index.mjs";
7
7
  import { generateHandler } from "../utils/generator.mjs";
8
- import { preserveNpmAliases, readPackageIdentity } from "../utils/npmAliases.mjs";
9
8
  const createNodePreset = ({ appContext, modernConfig })=>{
10
9
  const { appDirectory, distDirectory, moduleType } = appContext;
11
10
  const isEsmProject = 'module' === moduleType;
@@ -38,7 +37,6 @@ const createNodePreset = ({ appContext, modernConfig })=>{
38
37
  const filter = (filePath)=>!filePath.startsWith(staticDirectory) && !filePath.endsWith('.map');
39
38
  const entry = isEsmProject ? await resolveESMDependency('@modern-js/prod-server') : require.resolve('@modern-js/prod-server');
40
39
  if (!entry) throw new Error('Cannot find @modern-js/prod-server');
41
- const prodServerPackage = await readPackageIdentity(entry);
42
40
  await nodeDepEmit({
43
41
  appDir: appDirectory,
44
42
  sourceDir: outputDirectory,
@@ -57,17 +55,6 @@ const createNodePreset = ({ appContext, modernConfig })=>{
57
55
  };
58
56
  }
59
57
  });
60
- await preserveNpmAliases({
61
- appDirectory,
62
- outputDirectory,
63
- implicitAliases: [
64
- {
65
- aliasName: '@modern-js/prod-server',
66
- targetName: prodServerPackage.name,
67
- targetVersion: prodServerPackage.version
68
- }
69
- ]
70
- });
71
58
  console.log('Static directory:', chalk.blue(node_path.relative(appDirectory, staticDirectory).replace(/\\/g, '/')));
72
59
  console.log("You can preview this build by", chalk.blue('node .output/index'));
73
60
  }
@@ -39,7 +39,8 @@ const serverBuild = ()=>({
39
39
  sourceDirs,
40
40
  distDir,
41
41
  tsconfigPath,
42
- moduleType
42
+ moduleType,
43
+ excludeFiles: api.getAppContext().serverCompileExcludedFiles
43
44
  });
44
45
  });
45
46
  }
@@ -1,5 +1,4 @@
1
1
  export * from './adapterBasic.js';
2
2
  export * from './adapterHtml.js';
3
- export * from './adapterPrecompress.js';
4
3
  export * from './adapterSSR.js';
5
4
  export * from './builderHooks.js';
@@ -1,3 +1,2 @@
1
- export * from './build-environment.js';
2
1
  export * from './default.js';
3
2
  export * from './initialize/index.js';
@@ -1,7 +1,6 @@
1
1
  import type { AppTools, CliPlugin } from './types/index.js';
2
2
  import { initAppContext } from './utils/initAppContext.js';
3
3
  export * from './defineConfig.js';
4
- export * from './presetUltramodern.js';
5
4
  export declare const appTools: () => CliPlugin<AppTools>;
6
5
  export { build } from './commands/build.js';
7
6
  export { deploy } from './commands/deploy.js';
@@ -6,7 +6,7 @@ export declare const serverAppContextTemplate: (appContext: AppToolsContext) =>
6
6
  apiDirectory: string;
7
7
  lambdaDirectory: string;
8
8
  metaName: string;
9
- bffRuntimeFramework: "effect" | "hono";
9
+ bffRuntimeFramework: "hono";
10
10
  };
11
11
  export type PluginItem = [string, Record<string, any> | undefined];
12
12
  export declare const genPluginImportsCode: (plugins: PluginItem[], isESM?: boolean) => string;
@@ -1,8 +1,6 @@
1
1
  import type { BuilderConfig } from '@modern-js/builder';
2
2
  import type { SSGConfig, SSGMultiEntryOptions } from '@modern-js/types';
3
3
  import type { UnwrapBuilderConfig } from '../utils.js';
4
- import type { PrecompressConfig } from './precompress.js';
5
- export type { PrecompressCodecOptions, PrecompressConfig } from './precompress.js';
6
4
  export interface OutputUserConfig extends UnwrapBuilderConfig<BuilderConfig, 'output'> {
7
5
  /**
8
6
  * Enable SSG for self-controlled routing or conventional routing.
@@ -34,12 +32,5 @@ export interface OutputUserConfig extends UnwrapBuilderConfig<BuilderConfig, 'ou
34
32
  * Specify the temporary directory for framework generated files.
35
33
  */
36
34
  tempDir?: string;
37
- /**
38
- * Generate precompressed static assets by using compression-webpack-plugin.
39
- * `true` means enabling both gzip and brotli with default options.
40
- * Disabled unless explicitly configured; `presetUltramodern` enables it.
41
- * @default false
42
- */
43
- precompress?: boolean | PrecompressConfig;
44
35
  }
45
36
  export type { SSGConfig, SSGMultiEntryOptions, SSGRouteOptions, SSGSingleEntryOptions, } from '@modern-js/types';
@@ -7,4 +7,4 @@ export type { CLIPluginExtends, RuntimePluginConfig, ServerPluginConfig };
7
7
  export type AppUserConfig = AppToolsUserConfig;
8
8
  export type AppNormalizedConfig = AppToolsNormalizedConfig;
9
9
  export type AppToolsAPI = CLIPluginAPI<AppTools>;
10
- export type { AppToolsContext, AppToolsExtendAPI, AppToolsExtendContext, AppToolsExtendHooks, AppToolsHooks as AppToolsFeatureHooks, } from './plugin.js';
10
+ export type { AfterBffCompileFn, AppToolsContext, AppToolsExtendAPI, AppToolsExtendContext, AppToolsExtendHooks, AppToolsHooks as AppToolsFeatureHooks, BeforeBffCompileFn, BffClientArtifact, BffClientArtifacts, BffCompilation, BffGeneratedEntries, BffGeneratedModule, BffGeneration, ModifyBffClientArtifactsFn, ModifyBffGeneratedEntriesFn, } from './plugin.js';
@@ -1,11 +1,59 @@
1
1
  import type { AppContext, AsyncHook, InternalContext, PluginHook, PluginHookTap, TransformFunction } from '@modern-js/plugin';
2
2
  import type { Hooks } from '@modern-js/plugin/cli';
3
- import type { Entrypoint, HtmlPartials, HtmlTemplates, NestedRouteForCli, PageRoute, RouteLegacy, ServerPlugin, ServerRoute } from '@modern-js/types';
3
+ import type { BffRuntimeFramework } from '@modern-js/plugin/server';
4
+ import type { Entrypoint, HtmlPartials, HtmlTemplates, HttpMethodDecider, NestedRouteForCli, PageRoute, RouteLegacy, ServerPlugin, ServerRoute } from '@modern-js/types';
4
5
  import type { EagerRouteComponentFilesByEntry } from '@modern-js/utils';
5
6
  import type { EnvironmentConfig } from '@rsbuild/core';
6
7
  import type { getHookRunners } from '../compat/hooks.js';
7
8
  import type { AppTools } from './index.js';
8
9
  import type { AppToolsNormalizedConfig, AppToolsUserConfig } from './config/index.js';
10
+ export interface BffCompilation {
11
+ readonly appDirectory: string;
12
+ readonly apiDirectory: string;
13
+ readonly sourceDirectories: readonly string[];
14
+ readonly outputDirectories: readonly string[];
15
+ readonly distDirectory: string;
16
+ readonly tsconfigPath?: string;
17
+ readonly moduleType: AppToolsContext['moduleType'];
18
+ }
19
+ export interface BffGeneration {
20
+ readonly appDirectory: string;
21
+ readonly apiDirectory: string;
22
+ readonly lambdaDirectory: string;
23
+ readonly existLambda: boolean;
24
+ readonly apiFiles: readonly string[];
25
+ readonly relativeDistPath: string;
26
+ readonly prefix: string;
27
+ readonly port?: number;
28
+ readonly requestId: string;
29
+ readonly requestCreator?: string;
30
+ readonly httpMethodDecider?: HttpMethodDecider;
31
+ }
32
+ export interface BffClientArtifact {
33
+ /** Canonical relative source path under generation.apiDirectory. */
34
+ readonly sourcePath: string;
35
+ readonly code: string;
36
+ readonly declaration: string;
37
+ }
38
+ export interface BffClientArtifacts {
39
+ readonly generation: BffGeneration;
40
+ additionalArtifacts: BffClientArtifact[];
41
+ }
42
+ export interface BffGeneratedModule {
43
+ readonly code: string;
44
+ readonly declaration: string;
45
+ }
46
+ export interface BffGeneratedEntries {
47
+ readonly generation: BffGeneration;
48
+ plugin: BffGeneratedModule;
49
+ runtime: BffGeneratedModule | null;
50
+ /** Direct dependencies required by the emitted SDK modules. */
51
+ packageDependencies: Record<string, string>;
52
+ }
53
+ export type BeforeBffCompileFn = (context: BffCompilation) => void | Promise<void>;
54
+ export type AfterBffCompileFn = (context: BffCompilation) => void | Promise<void>;
55
+ export type ModifyBffClientArtifactsFn = TransformFunction<BffClientArtifacts>;
56
+ export type ModifyBffGeneratedEntriesFn = TransformFunction<BffGeneratedEntries>;
9
57
  export type AfterPrepareFn = () => Promise<void> | void;
10
58
  export type CheckEntryPointFn = TransformFunction<{
11
59
  path: string;
@@ -64,6 +112,10 @@ export interface AppToolsExtendAPI {
64
112
  useHookRunners: () => ReturnType<typeof getHookRunners>;
65
113
  }
66
114
  export interface AppToolsExtendHooks extends Record<string, PluginHook<(...args: any[]) => any>> {
115
+ onBeforeBffCompile: AsyncHook<BeforeBffCompileFn>;
116
+ onAfterBffCompile: AsyncHook<AfterBffCompileFn>;
117
+ modifyBffClientArtifacts: AsyncHook<ModifyBffClientArtifactsFn>;
118
+ modifyBffGeneratedEntries: AsyncHook<ModifyBffGeneratedEntriesFn>;
67
119
  onAfterPrepare: AsyncHook<AfterPrepareFn>;
68
120
  deploy: AsyncHook<DeplpoyFn>;
69
121
  checkEntryPoint: AsyncHook<CheckEntryPointFn>;
@@ -88,6 +140,8 @@ export interface AppToolsExtendContext {
88
140
  runtimeConfigFile: string;
89
141
  serverPlugins: ServerPlugin[];
90
142
  moduleType: 'module' | 'commonjs';
143
+ /** Exact generated files excluded from server compilation. */
144
+ serverCompileExcludedFiles?: string[];
91
145
  /** Information for entry points */
92
146
  entrypoints: Entrypoint[];
93
147
  /** Selected entry points */
@@ -115,7 +169,7 @@ export interface AppToolsExtendContext {
115
169
  * Identification for bff runtime framework
116
170
  * @private
117
171
  */
118
- bffRuntimeFramework?: 'hono' | 'effect';
172
+ bffRuntimeFramework?: BffRuntimeFramework;
119
173
  /**
120
174
  * Route component files collected from the FINAL file-system routes (after all
121
175
  * `modifyFileSystemRoutes` consumers ran), keyed by entry name. Populated by
@@ -1,3 +1,4 @@
1
+ import type { BffRuntimeFramework } from '@modern-js/plugin/server';
1
2
  export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFile, options, tempDir, }: {
2
3
  metaName: string;
3
4
  appDirectory: string;
@@ -7,7 +8,7 @@ export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFil
7
8
  apiDir?: string;
8
9
  distDir?: string;
9
10
  sharedDir?: string;
10
- bffRuntimeFramework?: 'hono' | 'effect';
11
+ bffRuntimeFramework?: BffRuntimeFramework;
11
12
  };
12
13
  tempDir?: string;
13
14
  }) => {
@@ -27,5 +28,5 @@ export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFil
27
28
  apiOnly: boolean;
28
29
  internalDirAlias: string;
29
30
  internalSrcAlias: string;
30
- bffRuntimeFramework: "effect" | "hono";
31
+ bffRuntimeFramework: "hono";
31
32
  };
@@ -45,21 +45,33 @@ Please refer to the [Enable BFF](/guides/advanced-features/bff/function.md#enabl
45
45
 
46
46
  :::caution Install the Effect peers yourself
47
47
  `effect` and `@effect/opentelemetry` are **optional exact peer dependencies** of
48
- `@modern-js/plugin-bff`, not dependencies. The plugin no longer bundles a copy —
49
- Effect 4 derives `Context` / `Service` keys per module instance, so a bundled copy
50
- would give your app a second Effect identity. Before setting
51
- `runtimeFramework: 'effect'` or importing `@modern-js/plugin-bff/effect`,
52
- `/effect-server`, `/effect-edge` or `/effect-client`, install the exact cohort:
48
+ `@modern-js/bff-effect`. Install them in the application so its API modules and
49
+ the framework use the same Effect instance. Before setting
50
+ `runtimeFramework: 'effect'` or importing `@modern-js/bff-effect/effect`,
51
+ `/effect-edge` or `/effect-client`, install the exact cohort:
53
52
 
54
53
  ```bash
55
54
  pnpm add effect@4.0.0-rc.112 @effect/opentelemetry@4.0.0-rc.112
56
55
  ```
57
56
 
58
57
  The pin is exact because UltraModern ships Effect as one lockstep cohort. Apps
59
- using only `runtimeFramework: 'hono'` or the `./data-platform` lane need neither
60
- package.
58
+ using only native `runtimeFramework: 'hono'` or
59
+ `@modern-js/bff-effect/data-platform` need neither package.
61
60
  :::
62
61
 
62
+ Use `bffPlugin` from `@modern-js/plugin-bff-build-extensions` to register the
63
+ Effect runtime. It composes the native BFF plugin. Install
64
+ `@modern-js/bff-effect` and `@modern-js/plugin-bff-extensions` from the same
65
+ framework cohort as application production dependencies, so the adapter remains
66
+ available after development dependencies are removed. The build plugin can be a
67
+ development dependency. See the [runtime configuration example](/guides/advanced-features/bff/frameworks.md).
68
+
69
+ Native `@modern-js/plugin-bff/server` exports Hono APIs. For Node Effect APIs,
70
+ import framework helpers such as `defineEffectBff` from
71
+ `@modern-js/bff-effect/effect` and namespaces from the corresponding `effect/*`
72
+ modules. Worker handlers and worker request context use
73
+ `@modern-js/bff-effect/effect-edge`.
74
+
63
75
  Generated UltraModern workspaces use this runtime as the only generated HTTP API
64
76
  path. The API contract lives at `shared/api.ts`, the server runtime lives at
65
77
  `api/index.ts`, clients live under `src/api/*-client.ts`, and generated checks
@@ -171,7 +183,6 @@ export default defineConfig({
171
183
  endpoint: '/_data/batch',
172
184
  maxBatchSize: 16,
173
185
  maxBatchBytes: 64 * 1024,
174
- flushIntervalMs: 8,
175
186
  maxConcurrency: 4,
176
187
  requestTimeoutMs: 10000,
177
188
  allowedMethods: ['GET'],
@@ -205,9 +216,9 @@ export default defineConfig({
205
216
  });
206
217
  ```
207
218
 
208
- `batch.flushIntervalMs` controls the client-side micro-batch window in the generated Effect client. `maxConcurrency` and `requestTimeoutMs` are applied by the server batch gateway when dispatching items.
219
+ `maxConcurrency` and `requestTimeoutMs` control the server batch gateway. Native `HttpApiClient` calls send individual requests; they do not automatically batch requests.
209
220
 
210
- The generated `api.client.*` API only exists for loader-materialized `@api/index` imports. Directly importing the server entry (`api/index`) exposes the Effect BFF definition; its `client` property is a placeholder that fails on operation access.
221
+ Import a shared `HttpApi` contract and pass it to `HttpApiClient.make` or `makeEffectHttpApiClient`. The client is fully type-inferred; `defineEffectBff` exposes server handlers and does not contain a client.
211
222
 
212
223
  ## Effect cohort
213
224
 
@@ -242,7 +253,7 @@ Strict Effect APIs should test the declared `HttpApi` contract, not a raw
242
253
  request handler. Edge-compatible tests can use the framework helper:
243
254
 
244
255
  ```ts
245
- import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
256
+ import { createEffectBffTestHandler } from '@modern-js/bff-effect/effect-edge';
246
257
  import apiModule from '../api/index';
247
258
 
248
259
  const testApi = await createEffectBffTestHandler({
@@ -257,12 +268,9 @@ If you manually compose an Effect web handler in a low-level proof, provide the
257
268
  platform services explicitly:
258
269
 
259
270
  ```ts
260
- import {
261
- HttpApiBuilder,
262
- HttpRouter,
263
- HttpServer,
264
- Layer,
265
- } from '@modern-js/plugin-bff/effect-server';
271
+ import * as Layer from 'effect/Layer';
272
+ import { HttpRouter, HttpServer } from 'effect/unstable/http';
273
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
266
274
 
267
275
  const handler = HttpRouter.toWebHandler(
268
276
  HttpApiBuilder.layer(api).pipe(
@@ -282,13 +290,10 @@ current Effect v4 beta cohort, `HttpRouter.middleware(...)` returns a `Layer`
282
290
  directly:
283
291
 
284
292
  ```ts
285
- import {
286
- Effect,
287
- HttpApiBuilder,
288
- HttpMiddleware,
289
- HttpRouter,
290
- Layer,
291
- } from '@modern-js/plugin-bff/effect-server';
293
+ import * as Effect from 'effect/Effect';
294
+ import * as Layer from 'effect/Layer';
295
+ import { HttpMiddleware, HttpRouter } from 'effect/unstable/http';
296
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
292
297
 
293
298
  const corsLayer = HttpRouter.middleware(
294
299
  Effect.succeed(
@@ -15,7 +15,7 @@ Modern.js Effect API runtime now supports a request-envelope based data platform
15
15
 
16
16
  ## Runtime contract helpers
17
17
 
18
- Use `@modern-js/plugin-bff/data-platform` helpers to build and validate contracts:
18
+ Use `@modern-js/bff-effect/data-platform` helpers to build and validate contracts:
19
19
 
20
20
  ```ts
21
21
  import {
@@ -28,7 +28,7 @@ import {
28
28
  validateHydrationEnvelope,
29
29
  createInvalidationEvent,
30
30
  shouldApplyInvalidation,
31
- } from '@modern-js/plugin-bff/data-platform';
31
+ } from '@modern-js/bff-effect/data-platform';
32
32
  ```
33
33
 
34
34
  ## Effect runtime validation
@@ -2,10 +2,10 @@
2
2
 
3
3
  # Runtime Framework
4
4
 
5
- Modern.js supports two BFF runtime frameworks:
5
+ Modern.js and the UltraModern BFF extension provide two runtime frameworks:
6
6
 
7
- - `effect` (default): use [Effect HttpApi](https://effect.website/) runtime from `api/index`.
8
- - `hono`: use file-convention BFF handlers from `api/lambda/**`.
7
+ - `hono` is the native plugin default and uses file-convention handlers from `api/lambda/**`.
8
+ - `effect` is the UltraModern extension default and uses [Effect HttpApi](https://effect.website/) from `api/index`.
9
9
 
10
10
  `effect` and `hono` are strict runtime modes. There is no automatic fallback between them.
11
11
 
@@ -16,12 +16,18 @@ Generated UltraModern workspaces use strict Effect APIs: author HTTP APIs in
16
16
 
17
17
  ## Switch to Effect runtime
18
18
 
19
+ Use the fork build plugin below. It includes the native BFF plugin and registers
20
+ the Effect adapter. The application needs `@modern-js/bff-effect` and
21
+ `@modern-js/plugin-bff-extensions` as production dependencies from the same
22
+ framework cohort. Install the exact Effect peers described in
23
+ [`bff.effect`](/configure/app/bff/effect.md).
24
+
19
25
  ```ts title="modern.config.ts"
20
- import { bffPlugin } from '@modern-js/plugin-bff';
21
- import { defineConfig } from '@modern-js/app-tools';
26
+ import { bffPlugin } from '@modern-js/plugin-bff-build-extensions';
27
+ import { appTools, defineConfig } from '@modern-js/app-tools';
22
28
 
23
29
  export default defineConfig({
24
- plugins: [bffPlugin()],
30
+ plugins: [appTools(), bffPlugin()],
25
31
  bff: {
26
32
  runtimeFramework: 'effect',
27
33
  effect: {
@@ -43,7 +49,7 @@ import {
43
49
  HttpApiEndpoint,
44
50
  HttpApiGroup,
45
51
  Schema,
46
- } from '@modern-js/plugin-bff/effect-client';
52
+ } from '@modern-js/bff-effect/effect-client';
47
53
 
48
54
  export const bffApi = HttpApi.make('MyApi').add(
49
55
  HttpApiGroup.make('hello').add(
@@ -57,14 +63,12 @@ export const bffApi = HttpApi.make('MyApi').add(
57
63
  Implement your Effect API entry at `api/index.ts`:
58
64
 
59
65
  ```ts title="api/index.ts"
60
- import {
61
- Schema,
62
- Effect,
63
- HttpApiBuilder,
64
- defineEffectBff,
65
- Layer,
66
- ServiceMap,
67
- } from '@modern-js/plugin-bff/effect-server';
66
+ import { defineEffectBff } from '@modern-js/bff-effect/effect';
67
+ import * as Context from 'effect/Context';
68
+ import * as Effect from 'effect/Effect';
69
+ import * as Layer from 'effect/Layer';
70
+ import * as Schema from 'effect/Schema';
71
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
68
72
  import { bffApi } from '../shared/api';
69
73
 
70
74
  class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableError>()(
@@ -74,7 +78,7 @@ class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableErr
74
78
  },
75
79
  ) {}
76
80
 
77
- class GreetingService extends ServiceMap.Service<GreetingService>()('GreetingService', {
81
+ class GreetingService extends Context.Service<GreetingService>()('GreetingService', {
78
82
  make: Effect.succeed({
79
83
  hello: Effect.fn('GreetingService.hello')(function* () {
80
84
  if (Date.now() < 0) {
@@ -109,19 +113,27 @@ const layer = HttpApiBuilder.layer(bffApi).pipe(
109
113
  export default defineEffectBff({ api: bffApi, layer });
110
114
  ```
111
115
 
112
- Call Effect endpoints from browser code via `@api/index`:
116
+ Create a native, fully inferred client from the shared contract:
113
117
 
114
118
  ```ts title="src/routes/page.tsx"
115
- import api from '@api/index';
119
+ import { Effect, makeEffectHttpApiClient } from '@modern-js/bff-effect/effect-client';
120
+ import { bffApi } from '../../shared/api';
116
121
 
117
- const response = await api.client.hello.ping({});
122
+ const response = await Effect.runPromise(
123
+ makeEffectHttpApiClient(bffApi, { baseUrl: '/api' }).pipe(
124
+ Effect.flatMap(client => client.hello.ping({})),
125
+ ),
126
+ );
118
127
  ```
119
128
 
120
- The `api.client.*` surface is materialized by the BFF loader for `@api/index` imports. Do not import `api/index` directly and expect `client` to run in server code, scripts, or tests; direct entry imports expose the server runtime definition, and `client` is only a typed placeholder there.
129
+ Requests, responses, and declared errors are inferred from `bffApi`. No client generation or server-entry import is needed.
121
130
 
122
131
  For UltraModern, Effect `HttpApi` plus Effect BFF is the single blessed authored HTTP path. Use `HttpApi` endpoints with `query`, `params`, `payload`, `success`, and declared errors such as `HttpApiSchema.status(...)`; implement them with `HttpApiBuilder.group(...).handle(...)` and `HttpApiBuilder.layer(...).pipe(Layer.provide(...))`, then default-export the entry as `defineEffectBff({ api, layer })`. See `packages/server/bff-effect/tests/effect-edge-runtime.test.ts` for the live runtime shape.
123
132
 
124
- Hono and `api/lambda/**` are internal compatibility only and feature-frozen.
133
+ Native Hono applications import operators from `@modern-js/plugin-bff/server`.
134
+ UltraModern-generated applications keep their strict Effect API model. Worker
135
+ handlers use `@modern-js/bff-effect/effect-edge`, including its worker request
136
+ context exports; Node handlers use the Effect entry shown above.
125
137
 
126
138
  ### Getting Request Context
127
139
 
@@ -146,7 +158,7 @@ For more details, refer to [useHonoContext](/apis/app/runtime/bff/use-backend-co
146
158
  When getting cookies in BFF functions, you need to get the request context through `useHonoContext`, then use `c.req.header('cookie')` to get the Cookie string and parse it manually:
147
159
 
148
160
  ```ts title="api/lambda/cookies.ts"
149
- import { Api, Get } from '@modern-js/plugin-bff/hono-server';
161
+ import { Api, Get } from '@modern-js/plugin-bff/server';
150
162
  import { useHonoContext } from '@modern-js/server-runtime';
151
163
 
152
164
  // Helper function to parse Cookie string
@@ -189,7 +201,7 @@ The `c.req.cookie()` method does not exist in the current version. You need to u
189
201
  When using Hono as the runtime framework, you can define interfaces through [Api functions](/guides/advanced-features/bff/operators.md):
190
202
 
191
203
  ```ts title="api/lambda/user.ts"
192
- import { Api, Get, Query } from '@modern-js/plugin-bff/hono-server';
204
+ import { Api, Get, Query } from '@modern-js/plugin-bff/server';
193
205
  import { z } from 'zod';
194
206
 
195
207
  const QuerySchema = z.object({
@@ -218,7 +230,7 @@ For more details about Api functions and operators, refer to [Creating Extensibl
218
230
  Hono supports a rich middleware ecosystem, and you can use middleware in BFF functions:
219
231
 
220
232
  ```ts title="api/lambda/user.ts"
221
- import { Api, Get, Middleware } from '@modern-js/plugin-bff/hono-server';
233
+ import { Api, Get, Middleware } from '@modern-js/plugin-bff/server';
222
234
 
223
235
  export const getUser = Api(
224
236
  Get('/user'),
@@ -246,7 +246,7 @@ Parameters following the dynamic path are an object called `RequestOption`, whic
246
246
  In a standard function without dynamic routes, `RequestOption` can be obtained from the first parameter, for example:
247
247
 
248
248
  ```ts title="api/lambda/hello.ts"
249
- import type { RequestOption } from '@modern-js/plugin-bff/hono-server';
249
+ import type { RequestOption } from '@modern-js/plugin-bff/server';
250
250
 
251
251
  export async function post({
252
252
  query,
@@ -259,7 +259,7 @@ export async function post({
259
259
  Custom types can also be used here:
260
260
 
261
261
  ```ts title="api/lambda/hello.ts"
262
- import type { RequestOption } from '@modern-js/plugin-bff/hono-server';
262
+ import type { RequestOption } from '@modern-js/plugin-bff/server';
263
263
 
264
264
  type IQuery = {
265
265
  // some types
@@ -29,7 +29,7 @@ A BFF function created by the `Api` function consists of the following parts:
29
29
  The server can define the input parameters and types of the interface. Based on the types, the server will automatically perform type validation at runtime:
30
30
 
31
31
  ```typescript title="api/lambda/user.ts"
32
- import { Api, Post, Query, Data } from '@modern-js/plugin-bff/hono-server';
32
+ import { Api, Post, Query, Data } from '@modern-js/plugin-bff/server';
33
33
  import { z } from 'zod';
34
34
 
35
35
  const UserSchema = z.object({
@@ -78,7 +78,7 @@ addUser({
78
78
  As shown in the example below, you can specify the route and HTTP Method through the `Get` function:
79
79
 
80
80
  ```typescript title="api/lambda/user.ts"
81
- import { Api, Get, Query, Data } from '@modern-js/plugin-bff/hono-server';
81
+ import { Api, Get, Query, Data } from '@modern-js/plugin-bff/server';
82
82
 
83
83
  // Specify the interface route, Modern.js sets `bff.prefix` to `/api` by default,
84
84
  // so the interface route is `/api/user`, and the HTTP Method is GET.
@@ -92,7 +92,7 @@ export const getHello = Api(
92
92
  When the route is not specified, the interface route is defined according to the file convention. As shown in the example below, with the function writing method, there is a code path `api/lambda/user.ts`, which will register the corresponding interface `/api/user`.
93
93
 
94
94
  ```typescript title="api/lambda/user.ts"
95
- import { Api, Get, Query, Data } from '@modern-js/plugin-bff/hono-server';
95
+ import { Api, Get, Query, Data } from '@modern-js/plugin-bff/server';
96
96
 
97
97
  // No interface route specified, according to file convention and function name, the interface is api/user, HTTP Method is get.
98
98
  export const get = Api(Query(UserSchema), async ({ query }) => query);
@@ -125,7 +125,7 @@ Using the `Query` function, you can define the type of query. After using the `Q
125
125
 
126
126
  ```typescript title="api/lambda/user.ts"
127
127
  // Server-side code
128
- import { Api, Query } from '@modern-js/plugin-bff/hono-server';
128
+ import { Api, Query } from '@modern-js/plugin-bff/server';
129
129
  import { z } from 'zod';
130
130
 
131
131
  const UserSchema = z.object({
@@ -153,7 +153,7 @@ get({
153
153
  URL query parameters are strings by default. If you need numeric types, you need to use `z.coerce.number()` for type conversion:
154
154
 
155
155
  ```typescript title="api/lambda/user.ts"
156
- import { Api, Get, Query } from '@modern-js/plugin-bff/hono-server';
156
+ import { Api, Get, Query } from '@modern-js/plugin-bff/server';
157
157
  import { z } from 'zod';
158
158
 
159
159
  const QuerySchema = z.object({
@@ -189,7 +189,7 @@ If you use the Data function, you must follow the HTTP protocol. When the HTTP M
189
189
  :::
190
190
 
191
191
  ```typescript title="api/lambda/user.ts"
192
- import { Api, Data } from '@modern-js/plugin-bff/hono-server';
192
+ import { Api, Data } from '@modern-js/plugin-bff/server';
193
193
  import { z } from 'zod';
194
194
 
195
195
  const DataSchema = z.object({
@@ -218,7 +218,7 @@ post({
218
218
  Route parameters can implement dynamic routes and get parameters from the path. You can specify path parameters through `Params<T>(schema: z.ZodType<T>)`
219
219
 
220
220
  ```typescript
221
- import { Api, Get, Params } from '@modern-js/plugin-bff/hono-server';
221
+ import { Api, Get, Params } from '@modern-js/plugin-bff/server';
222
222
  import { z } from 'zod';
223
223
 
224
224
  const UserSchema = z.object({
@@ -239,7 +239,7 @@ export const queryUser = Api(
239
239
  You can define the request headers required by the interface through the `Headers<T>(schema: z.ZodType<T>)` function and pass the request headers through integrated calls:
240
240
 
241
241
  ```typescript
242
- import { Api, Headers } from '@modern-js/plugin-bff/hono-server';
242
+ import { Api, Headers } from '@modern-js/plugin-bff/server';
243
243
  import { z } from 'zod';
244
244
 
245
245
  const headerSchema = z.object({
@@ -297,7 +297,7 @@ The `Middleware` operator can be configured multiple times, and the execution or
297
297
  :::
298
298
 
299
299
  ```typescript
300
- import { Api, Query, Middleware } from '@modern-js/plugin-bff/hono-server';
300
+ import { Api, Query, Middleware } from '@modern-js/plugin-bff/server';
301
301
  import { z } from 'zod';
302
302
 
303
303
  const UserSchema = z.object({
@@ -333,7 +333,7 @@ The `Pipe` operator can be configured multiple times. The execution order of fun
333
333
  :::
334
334
 
335
335
  ```typescript
336
- import { Api, Query, Pipe } from '@modern-js/plugin-bff/hono-server';
336
+ import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
337
337
  import { z } from 'zod';
338
338
 
339
339
  const UserSchema = z.object({
@@ -361,7 +361,7 @@ export const get = Api(
361
361
  Also,
362
362
 
363
363
  ```typescript
364
- import { Api, Query, Pipe } from '@modern-js/plugin-bff/hono-server';
364
+ import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
365
365
  import { z } from 'zod';
366
366
 
367
367
  const UserSchema = z.object({
@@ -392,7 +392,7 @@ export const get = Api(
392
392
  If you need to do more custom operations on the response, you can pass a function to the `end` function. The input parameter of the function is Hono's Context (`c`), and you can operate on `c.req` and `c.res`:
393
393
 
394
394
  ```typescript
395
- import { Api, Query, Pipe } from '@modern-js/plugin-bff/hono-server';
395
+ import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
396
396
  import { z } from 'zod';
397
397
 
398
398
  const UserSchema = z.object({
@@ -432,7 +432,7 @@ The following are response-related operators. Through response operators, you ca
432
432
  You can specify the status code returned by the interface through the `HttpCode(statusCode: number)` function
433
433
 
434
434
  ```typescript
435
- import { Api, Query, Data, HttpCode } from '@modern-js/plugin-bff/hono-server';
435
+ import { Api, Query, Data, HttpCode } from '@modern-js/plugin-bff/server';
436
436
  import { z } from 'zod';
437
437
 
438
438
  const UserSchema = z.object({
@@ -464,7 +464,7 @@ export const post = Api(
464
464
  Supports setting response headers through the `SetHeaders(headers: Record<string, string>)` function
465
465
 
466
466
  ```typescript
467
- import { Api, Get, SetHeaders } from '@modern-js/plugin-bff/hono-server';
467
+ import { Api, Get, SetHeaders } from '@modern-js/plugin-bff/server';
468
468
 
469
469
  export default Api(
470
470
  Get('/hello'),
@@ -480,7 +480,7 @@ export default Api(
480
480
  Supports redirecting the interface through `Redirect(url: string)`:
481
481
 
482
482
  ```typescript
483
- import { Api, Get, Redirect } from '@modern-js/plugin-bff/hono-server';
483
+ import { Api, Get, Redirect } from '@modern-js/plugin-bff/server';
484
484
 
485
485
  export default Api(
486
486
  Get('/hello'),
@@ -494,7 +494,7 @@ export default Api(
494
494
  As mentioned above, through operators, you can get `query`, `data`, `params`, etc. in the input parameters of the interface processing function. But sometimes we need to get more request context information. At this time, we can get it through [`useHonoContext`](/apis/app/runtime/bff/use-backend-context.md):
495
495
 
496
496
  ```typescript title="api/lambda/user.ts"
497
- import { Api, Get, Query } from '@modern-js/plugin-bff/hono-server';
497
+ import { Api, Get, Query } from '@modern-js/plugin-bff/server';
498
498
  import { useHonoContext } from '@modern-js/server-runtime';
499
499
  import { z } from 'zod';
500
500
 
@@ -539,7 +539,7 @@ For specific comparisons of different solutions, you can refer to [Why Use Zod](
539
539
  In frontend development, some server interfaces (such as some configuration interfaces) have long response times, but actually don't need to be updated for a long time. For such interfaces, we can set HTTP cache to improve page performance:
540
540
 
541
541
  ```typescript
542
- import { Api, SetHeaders } from '@modern-js/plugin-bff/hono-server';
542
+ import { Api, SetHeaders } from '@modern-js/plugin-bff/server';
543
543
 
544
544
  export const get = Api(
545
545
  // Cache will only take effect when using integrated calls or fetch for requests