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

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 +27 -21
  28. package/docs/guides/advanced-features/bff/data-platform.md +2 -2
  29. package/docs/guides/advanced-features/bff/frameworks.md +27 -20
  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 +1 -1
  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;
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
@@ -242,7 +254,7 @@ Strict Effect APIs should test the declared `HttpApi` contract, not a raw
242
254
  request handler. Edge-compatible tests can use the framework helper:
243
255
 
244
256
  ```ts
245
- import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
257
+ import { createEffectBffTestHandler } from '@modern-js/bff-effect/effect-edge';
246
258
  import apiModule from '../api/index';
247
259
 
248
260
  const testApi = await createEffectBffTestHandler({
@@ -257,12 +269,9 @@ If you manually compose an Effect web handler in a low-level proof, provide the
257
269
  platform services explicitly:
258
270
 
259
271
  ```ts
260
- import {
261
- HttpApiBuilder,
262
- HttpRouter,
263
- HttpServer,
264
- Layer,
265
- } from '@modern-js/plugin-bff/effect-server';
272
+ import * as Layer from 'effect/Layer';
273
+ import { HttpRouter, HttpServer } from 'effect/unstable/http';
274
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
266
275
 
267
276
  const handler = HttpRouter.toWebHandler(
268
277
  HttpApiBuilder.layer(api).pipe(
@@ -282,13 +291,10 @@ current Effect v4 beta cohort, `HttpRouter.middleware(...)` returns a `Layer`
282
291
  directly:
283
292
 
284
293
  ```ts
285
- import {
286
- Effect,
287
- HttpApiBuilder,
288
- HttpMiddleware,
289
- HttpRouter,
290
- Layer,
291
- } from '@modern-js/plugin-bff/effect-server';
294
+ import * as Effect from 'effect/Effect';
295
+ import * as Layer from 'effect/Layer';
296
+ import { HttpMiddleware, HttpRouter } from 'effect/unstable/http';
297
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
292
298
 
293
299
  const corsLayer = HttpRouter.middleware(
294
300
  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) {
@@ -121,7 +125,10 @@ The `api.client.*` surface is materialized by the BFF loader for `@api/index` im
121
125
 
122
126
  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
127
 
124
- Hono and `api/lambda/**` are internal compatibility only and feature-frozen.
128
+ Native Hono applications import operators from `@modern-js/plugin-bff/server`.
129
+ UltraModern-generated applications keep their strict Effect API model. Worker
130
+ handlers use `@modern-js/bff-effect/effect-edge`, including its worker request
131
+ context exports; Node handlers use the Effect entry shown above.
125
132
 
126
133
  ### Getting Request Context
127
134
 
@@ -146,7 +153,7 @@ For more details, refer to [useHonoContext](/apis/app/runtime/bff/use-backend-co
146
153
  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
154
 
148
155
  ```ts title="api/lambda/cookies.ts"
149
- import { Api, Get } from '@modern-js/plugin-bff/hono-server';
156
+ import { Api, Get } from '@modern-js/plugin-bff/server';
150
157
  import { useHonoContext } from '@modern-js/server-runtime';
151
158
 
152
159
  // Helper function to parse Cookie string
@@ -189,7 +196,7 @@ The `c.req.cookie()` method does not exist in the current version. You need to u
189
196
  When using Hono as the runtime framework, you can define interfaces through [Api functions](/guides/advanced-features/bff/operators.md):
190
197
 
191
198
  ```ts title="api/lambda/user.ts"
192
- import { Api, Get, Query } from '@modern-js/plugin-bff/hono-server';
199
+ import { Api, Get, Query } from '@modern-js/plugin-bff/server';
193
200
  import { z } from 'zod';
194
201
 
195
202
  const QuerySchema = z.object({
@@ -218,7 +225,7 @@ For more details about Api functions and operators, refer to [Creating Extensibl
218
225
  Hono supports a rich middleware ecosystem, and you can use middleware in BFF functions:
219
226
 
220
227
  ```ts title="api/lambda/user.ts"
221
- import { Api, Get, Middleware } from '@modern-js/plugin-bff/hono-server';
228
+ import { Api, Get, Middleware } from '@modern-js/plugin-bff/server';
222
229
 
223
230
  export const getUser = Api(
224
231
  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
@@ -125,11 +125,7 @@ Now, the project structure is as follows:
125
125
  ```
126
126
 
127
127
  The default workspace starts shell-only and installs the published BleedingDev
128
- package aliases:
129
-
130
- ```bash
131
- pnpm dlx @bleedingdev/modern-js-ultramodern-create my-super-app
132
- ```
128
+ package aliases.
133
129
 
134
130
  From a generated SuperApp workspace, add a business MicroVertical in place:
135
131
 
@@ -210,7 +210,7 @@ transport surfaces when needed. They do not make raw request handlers valid
210
210
  inside generated HTTP API modules.
211
211
 
212
212
  Strict API tests should exercise the `HttpApi` contract. Use
213
- `createEffectBffTestHandler` from `@modern-js/plugin-bff/effect-edge` for
213
+ `createEffectBffTestHandler` from `@modern-js/bff-effect/effect-edge` for
214
214
  edge-compatible proof tests; if you manually compose a web handler, provide
215
215
  `HttpServer.layerServices` beside your API group layer before calling
216
216
  `HttpRouter.toWebHandler`.