@bleedingdev/modern-js-main-doc 3.4.0-ultramodern.19 → 3.4.0-ultramodern.20
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.
- package/docs/en/configure/app/bff/effect.mdx +11 -3
- package/docs/en/configure/app/bff/runtime-framework.mdx +2 -2
- package/docs/en/guides/advanced-features/bff/data-platform.mdx +4 -4
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +16 -14
- package/docs/en/guides/advanced-features/bff/function.mdx +4 -0
- package/docs/en/guides/get-started/ultramodern.mdx +49 -14
- package/docs/zh/configure/app/bff/effect.mdx +11 -3
- package/docs/zh/configure/app/bff/runtime-framework.mdx +2 -2
- package/docs/zh/guides/advanced-features/bff/data-platform.mdx +2 -2
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +16 -14
- package/docs/zh/guides/get-started/ultramodern.mdx +37 -6
- package/package.json +2 -2
|
@@ -9,6 +9,7 @@ title: effect
|
|
|
9
9
|
```ts
|
|
10
10
|
type BffEffectUserConfig = {
|
|
11
11
|
entry?: string;
|
|
12
|
+
strictEffectApproach?: true;
|
|
12
13
|
openapi?: boolean | { path?: string };
|
|
13
14
|
dataPlatform?: {
|
|
14
15
|
enabled?: boolean;
|
|
@@ -58,7 +59,7 @@ For Effect v4, TypeScript should use export-map aware module resolution.
|
|
|
58
59
|
## bff.effect.entry
|
|
59
60
|
|
|
60
61
|
- **Type:** `string`
|
|
61
|
-
- **Default:** `<apiDirectory>/
|
|
62
|
+
- **Default:** `<apiDirectory>/index`
|
|
62
63
|
|
|
63
64
|
Specifies the entry module for Effect HttpApi runtime.
|
|
64
65
|
|
|
@@ -67,12 +68,19 @@ export default defineConfig({
|
|
|
67
68
|
bff: {
|
|
68
69
|
runtimeFramework: 'effect',
|
|
69
70
|
effect: {
|
|
70
|
-
entry: './api/
|
|
71
|
+
entry: './api/index',
|
|
71
72
|
},
|
|
72
73
|
},
|
|
73
74
|
});
|
|
74
75
|
```
|
|
75
76
|
|
|
77
|
+
## bff.effect.strictEffectApproach
|
|
78
|
+
|
|
79
|
+
- **Type:** `true`
|
|
80
|
+
- **Default:** `true`
|
|
81
|
+
|
|
82
|
+
Effect BFF modules must expose the strict Effect API surface (`defineEffectBff(...)` or `{ api, layer }`) and raw request handlers are rejected. Generated UltraModern apps write this marker explicitly.
|
|
83
|
+
|
|
76
84
|
## bff.effect.openapi
|
|
77
85
|
|
|
78
86
|
- **Type:** `boolean | { path?: string }`
|
|
@@ -173,4 +181,4 @@ export default defineConfig({
|
|
|
173
181
|
|
|
174
182
|
`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.
|
|
175
183
|
|
|
176
|
-
The generated `
|
|
184
|
+
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.
|
|
@@ -21,8 +21,8 @@ export default defineConfig({
|
|
|
21
21
|
});
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
- Set to `'effect'` to run BFF only from `api/
|
|
24
|
+
- Set to `'effect'` to run BFF only from `api/index` (Effect HttpApi runtime).
|
|
25
25
|
- Set to `'hono'` to run BFF only from file-convention handlers under `api/lambda/**`.
|
|
26
26
|
|
|
27
27
|
There is no implicit fallback between runtimes. Choose one runtime mode per app.
|
|
28
|
-
Generated UltraModern apps use
|
|
28
|
+
Generated UltraModern apps use strict Effect APIs by default and set `bff.effect.strictEffectApproach: true`.
|
|
@@ -4,7 +4,7 @@ title: Data Platform
|
|
|
4
4
|
|
|
5
5
|
# Data Platform
|
|
6
6
|
|
|
7
|
-
Modern.js Effect
|
|
7
|
+
Modern.js Effect API runtime now supports a request-envelope based data platform contract for safer and more predictable data orchestration in complex applications such as Module Federation and micro frontends.
|
|
8
8
|
|
|
9
9
|
## What it solves
|
|
10
10
|
|
|
@@ -57,11 +57,11 @@ export default defineConfig({
|
|
|
57
57
|
|
|
58
58
|
Envelope validation is enabled by default but does not require an envelope unless `requireEnvelope` is set. Origin validation is enabled by default; set `validateOrigin: false` to skip comparing the envelope origin against the incoming `Origin` header, or the request URL origin when the header is absent.
|
|
59
59
|
|
|
60
|
-
## Generated
|
|
60
|
+
## Generated API client behavior
|
|
61
61
|
|
|
62
|
-
When using the generated
|
|
62
|
+
When using the generated API client (`@api/index`), Modern.js automatically attaches a serialized data envelope header (`x-modernjs-data-envelope`) for same-origin requests.
|
|
63
63
|
|
|
64
|
-
The generated client is loader-materialized. Import it from `@api/
|
|
64
|
+
The generated client is loader-materialized. Import it from `@api/index` in browser or client-bundled code; direct imports of the server entry (`api/index`) expose the Effect API definition and only provide a placeholder `client`.
|
|
65
65
|
|
|
66
66
|
The envelope includes:
|
|
67
67
|
|
|
@@ -7,7 +7,7 @@ title: Runtime Framework
|
|
|
7
7
|
|
|
8
8
|
Modern.js supports two BFF runtime frameworks:
|
|
9
9
|
|
|
10
|
-
- `effect` (default): use [Effect HttpApi](https://effect.website/) runtime from `api/
|
|
10
|
+
- `effect` (default): use [Effect HttpApi](https://effect.website/) runtime from `api/index`.
|
|
11
11
|
- `hono`: use file-convention BFF handlers from `api/lambda/**`.
|
|
12
12
|
|
|
13
13
|
`effect` and `hono` are strict runtime modes. There is no automatic fallback between them.
|
|
@@ -23,6 +23,8 @@ export default defineConfig({
|
|
|
23
23
|
bff: {
|
|
24
24
|
runtimeFramework: 'effect',
|
|
25
25
|
effect: {
|
|
26
|
+
entry: './api/index',
|
|
27
|
+
strictEffectApproach: true,
|
|
26
28
|
openapi: {
|
|
27
29
|
path: '/openapi.json',
|
|
28
30
|
},
|
|
@@ -33,7 +35,7 @@ export default defineConfig({
|
|
|
33
35
|
|
|
34
36
|
Define a shared Effect HttpApi contract (for type-safe client + server):
|
|
35
37
|
|
|
36
|
-
```ts title="shared/
|
|
38
|
+
```ts title="shared/api.ts"
|
|
37
39
|
import {
|
|
38
40
|
HttpApi,
|
|
39
41
|
HttpApiEndpoint,
|
|
@@ -41,18 +43,18 @@ import {
|
|
|
41
43
|
Schema,
|
|
42
44
|
} from '@modern-js/plugin-bff/effect-client';
|
|
43
45
|
|
|
44
|
-
export const
|
|
46
|
+
export const bffApi = HttpApi.make('MyApi').add(
|
|
45
47
|
HttpApiGroup.make('hello').add(
|
|
46
|
-
HttpApiEndpoint.get('ping', '/
|
|
48
|
+
HttpApiEndpoint.get('ping', '/ping', {
|
|
47
49
|
success: Schema.String,
|
|
48
50
|
}),
|
|
49
51
|
),
|
|
50
52
|
);
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
Implement your Effect
|
|
55
|
+
Implement your Effect API entry at `api/index.ts`:
|
|
54
56
|
|
|
55
|
-
```ts title="api/
|
|
57
|
+
```ts title="api/index.ts"
|
|
56
58
|
import {
|
|
57
59
|
Schema,
|
|
58
60
|
Effect,
|
|
@@ -61,7 +63,7 @@ import {
|
|
|
61
63
|
Layer,
|
|
62
64
|
ServiceMap,
|
|
63
65
|
} from '@modern-js/plugin-bff/effect-server';
|
|
64
|
-
import {
|
|
66
|
+
import { bffApi } from '../shared/api';
|
|
65
67
|
|
|
66
68
|
class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableError>()(
|
|
67
69
|
'GreetingUnavailableError',
|
|
@@ -87,7 +89,7 @@ class GreetingService extends ServiceMap.Service<GreetingService>()('GreetingSer
|
|
|
87
89
|
static readonly layer = Layer.effect(this, this.make);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
const group = HttpApiBuilder.group(
|
|
92
|
+
const group = HttpApiBuilder.group(bffApi, 'hello', handlers =>
|
|
91
93
|
handlers.handle('ping', () =>
|
|
92
94
|
GreetingService.use(service => service.hello()).pipe(
|
|
93
95
|
Effect.catchTag('GreetingUnavailableError', error =>
|
|
@@ -97,23 +99,23 @@ const group = HttpApiBuilder.group(bffEffectApi, 'hello', handlers =>
|
|
|
97
99
|
),
|
|
98
100
|
);
|
|
99
101
|
|
|
100
|
-
const layer = HttpApiBuilder.layer(
|
|
102
|
+
const layer = HttpApiBuilder.layer(bffApi).pipe(
|
|
101
103
|
Layer.provide(group),
|
|
102
104
|
Layer.provide(GreetingService.layer),
|
|
103
105
|
);
|
|
104
106
|
|
|
105
|
-
export default defineEffectBff({ api:
|
|
107
|
+
export default defineEffectBff({ api: bffApi, layer });
|
|
106
108
|
```
|
|
107
109
|
|
|
108
|
-
Call Effect endpoints from browser code via `@api/
|
|
110
|
+
Call Effect endpoints from browser code via `@api/index`:
|
|
109
111
|
|
|
110
112
|
```ts title="src/routes/page.tsx"
|
|
111
|
-
import
|
|
113
|
+
import api from '@api/index';
|
|
112
114
|
|
|
113
|
-
const response = await
|
|
115
|
+
const response = await api.client.hello.ping({});
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
The `
|
|
118
|
+
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.
|
|
117
119
|
|
|
118
120
|
import Hono from '@site-docs-en/components/hono';
|
|
119
121
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
In a Modern.js application, developers can define API files under the `api/lambda` directory and export API functions from these files. In the frontend code, these API functions can be directly invoked by importing the file, which initiates the API requests.
|
|
4
4
|
|
|
5
|
+
:::warning
|
|
6
|
+
`api/lambda/**` is the Hono/file-function compatibility runtime. New UltraModern generated API work should use the strict Effect runtime with `api/index.ts`, `shared/api.ts`, `defineEffectBff(...)`, and Effect `HttpApi` schemas instead.
|
|
7
|
+
:::
|
|
8
|
+
|
|
5
9
|
This invocation method is called **unified invocation**, where developers do not need to write glue code for the frontend and backend separately, thereby ensuring type safety across both.
|
|
6
10
|
|
|
7
11
|
## Enable BFF
|
|
@@ -33,7 +33,7 @@ UltraModern.js additions are designed as the default product surface for new Sup
|
|
|
33
33
|
| Build diagnostics | RsDoctor is generally opt-in | Adds a first-class `performance.rsdoctor` config surface (opt-in; the earlier default-on behavior and diagnostics contract artifact were reverted) |
|
|
34
34
|
| Output and static serving | Precompression behavior is app-defined | Enables `output.precompress` by default and serves `.br` / `.gz` variants via `Accept-Encoding` negotiation |
|
|
35
35
|
| BFF runtime and contracts | Standard BFF runtime/client generation | Adds `requestId`-aware producer isolation, fail-fast initialization checks, and operation/trace correlation headers |
|
|
36
|
-
| BFF runtime choices | Hono runtime path only in Modern.js 3.0 baseline (no built-in Effect runtime path) | Sets Effect as
|
|
36
|
+
| BFF runtime choices | Hono runtime path only in Modern.js 3.0 baseline (no built-in Effect runtime path) | Sets Effect as the strict default API runtime (`api/index.ts`, `shared/api.ts`, `src/api/*`) and keeps raw Hono handlers as an explicit compatibility lane |
|
|
37
37
|
| Telemetry standardization | Observability wiring is often app-specific | Adds framework-level telemetry pipeline with OTLP/VictoriaMetrics exporters, redaction, batching, and backpressure controls |
|
|
38
38
|
| App-level MF SSR handshake | No dedicated super-app app-level stability contract focus | Adds `server.ssr.moduleFederationAppSSR` plus integration-tested env/config handshake |
|
|
39
39
|
| MF vertical loading reliability | Retry/fallback patterns are often implemented per app | Adds deterministic timeout/network/contract-error reliability matrix and distributed OTEL continuity tests |
|
|
@@ -46,16 +46,16 @@ UltraModern.js additions are designed as the default product surface for new Sup
|
|
|
46
46
|
|
|
47
47
|
- We do not hide the fork behind legacy Modern.js branding.
|
|
48
48
|
- We do not optimize for generic Modern.js defaults when they conflict with SuperApp reliability.
|
|
49
|
-
-
|
|
50
|
-
- We
|
|
49
|
+
- Generated UltraModern API work uses the Effect runtime only; raw Hono/function handlers are not part of the generated API architecture.
|
|
50
|
+
- We make incompatible scaffold changes when they remove architecture drift.
|
|
51
51
|
|
|
52
52
|
## Migration Guide
|
|
53
53
|
|
|
54
|
-
For teams already on Modern.js 3.0 or an older BleedingDev UltraModern scaffold, the adoption path
|
|
54
|
+
For teams already on Modern.js 3.0 or an older BleedingDev UltraModern scaffold, the adoption path is to move API work onto the strict Effect API surface instead of preserving older raw handler layouts.
|
|
55
55
|
|
|
56
56
|
1. Keep existing React Router apps running as-is. TanStack Router is the preferred path for new scaffolds and incremental route adoption, but the React Router lane remains supported while teams move on their own schedule.
|
|
57
|
-
2.
|
|
58
|
-
3. Treat
|
|
57
|
+
2. Use `bff.runtimeFramework: 'effect'` with `bff.effect.strictEffectApproach: true` for API work. Entries live at `api/index.ts`, contracts live at `shared/api.ts`, clients live under `src/api/*`, and request/response/error shapes come from Effect `Schema` plus `HttpApi`.
|
|
58
|
+
3. Treat raw handlers, `api/lambda/**`, manual `Response` construction, and manual request parsing as migration defects in generated UltraModern workspaces.
|
|
59
59
|
4. The public preset now ships with explicit release and certification gates. Generated workspaces include `.github/workflows/ultramodern-workspace-gates.yml`, so `pnpm check` and `pnpm build` stay part of the local adoption contract from day one while CI runs the primitive gates as parallel matrix jobs.
|
|
60
60
|
|
|
61
61
|
For an older generated workspace, migrate by treating the published cohort as
|
|
@@ -71,14 +71,49 @@ mise exec -- pnpm check
|
|
|
71
71
|
mise exec -- pnpm build
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
Strict Effect API migration is not compatible with
|
|
75
|
+
`3.4.0-ultramodern.19` or older package cohorts. Those packages do not include
|
|
76
|
+
the `strictEffectApproach` config type or the direct `api/index.ts`
|
|
77
|
+
generator/checks. Until a newer BleedingDev cohort is published, use the local
|
|
78
|
+
Modern.js workspace for migration validation; after publication, pin that exact
|
|
79
|
+
new cohort with `--ultramodern-package-version`.
|
|
80
|
+
|
|
81
|
+
When migrating a generated workspace that already has Effect API files, make the
|
|
82
|
+
move explicit and do not keep compatibility aliases:
|
|
83
|
+
|
|
84
|
+
1. Move each vertical server entry from `verticals/<id>/api/effect/index.ts` to
|
|
85
|
+
`verticals/<id>/api/index.ts`.
|
|
86
|
+
2. Move each shared API contract from `verticals/<id>/shared/effect/api.ts` to
|
|
87
|
+
`verticals/<id>/shared/api.ts`.
|
|
88
|
+
3. Move generated clients from `verticals/<id>/src/effect/*-client.ts` to
|
|
89
|
+
`verticals/<id>/src/api/*-client.ts`.
|
|
90
|
+
4. Move shell API aggregates from `apps/shell-super-app/src/effect/*` to
|
|
91
|
+
`apps/shell-super-app/src/api/*`.
|
|
92
|
+
5. Update imports and package exports to use `./api`, `./api/client`, and
|
|
93
|
+
`@<workspace>/<vertical>/api/client`; remove `./effect/client`,
|
|
94
|
+
`./shared/effect/api`, and shared Effect API packages.
|
|
95
|
+
6. Update every vertical `modern.config.ts` to use
|
|
96
|
+
`bff.effect.entry: './api/index'` and
|
|
97
|
+
`bff.effect.strictEffectApproach: true`.
|
|
98
|
+
7. Update topology so API metadata lives directly under `api` with
|
|
99
|
+
`api.bff.strictEffectApproach: true`; do not keep `api.effect`.
|
|
100
|
+
8. Run `pnpm api:check`,
|
|
101
|
+
`scripts/validate-ultramodern-workspace.mjs`, `pnpm check`, and
|
|
102
|
+
`pnpm build`.
|
|
103
|
+
|
|
104
|
+
If a migration fails, fix the owning generated contract, topology, package
|
|
105
|
+
exports, or API module. Do not add app-level aliases, raw request handlers,
|
|
106
|
+
manual `Response` construction, local type casts, or package shims to make the
|
|
107
|
+
old layout pass.
|
|
108
|
+
|
|
74
109
|
Use one package-source strategy per repo. The published BleedingDev create
|
|
75
110
|
package defaults to `--ultramodern-package-source=install` and records the
|
|
76
|
-
exact cohort in `.modernjs/ultramodern
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
111
|
+
exact cohort in `.modernjs/ultramodern.json`. Keep `--workspace` only for local
|
|
112
|
+
monorepo testing against unreleased packages. Release proof and CI should pin
|
|
113
|
+
the exact cohort with `--ultramodern-package-version` when a repo must prove a
|
|
114
|
+
specific published framework version.
|
|
80
115
|
|
|
81
|
-
Older repos with Effect
|
|
116
|
+
Older repos with nested Effect entries and `verbatimModuleSyntax` should update to
|
|
82
117
|
the latest BleedingDev cohort instead of adding app-level `"type": "module"`
|
|
83
118
|
metadata, Module Federation shims, or custom server wrappers. The framework BFF
|
|
84
119
|
compiler normalizes CommonJS server output while generated app packages keep
|
|
@@ -239,8 +274,8 @@ non-indexable routes emit no JSON-LD by default. Public route owners can add
|
|
|
239
274
|
`jsonLd` beside localized paths and use the generated
|
|
240
275
|
`src/routes/ultramodern-jsonld.ts` helpers for common schema.org shapes.
|
|
241
276
|
|
|
242
|
-
The generated contract writes `.modernjs/ultramodern
|
|
243
|
-
|
|
277
|
+
The generated contract writes `.modernjs/ultramodern.json` with a
|
|
278
|
+
`cssFederation` section:
|
|
244
279
|
|
|
245
280
|
- `packages/shared-design-tokens` owns the shared token layer and exports `./tokens.css`.
|
|
246
281
|
- Shell CSS owns only shell base and overlay layers under `[data-app-id="shell-super-app"]`.
|
|
@@ -248,7 +283,7 @@ with a `cssFederation` section:
|
|
|
248
283
|
- Tailwind CSS v4 is local to each generated app through `@tailwindcss/postcss`; shared base styles must not be duplicated by verticals.
|
|
249
284
|
- SSR first paint requires shared token CSS and app-owned CSS to be emitted by Modern/Rspack assets. Vertical CSS is loaded through manifest ownership, not copied into shell source.
|
|
250
285
|
|
|
251
|
-
Version switching must select UI,
|
|
286
|
+
Version switching must select UI, API, CSS, i18n JSON, and MF manifest evidence from the same vertical build marker. A shell render that only changes the UI marker is not enough.
|
|
252
287
|
|
|
253
288
|
### Validation And Deploy
|
|
254
289
|
|
|
@@ -9,6 +9,7 @@ title: effect
|
|
|
9
9
|
```ts
|
|
10
10
|
type BffEffectUserConfig = {
|
|
11
11
|
entry?: string;
|
|
12
|
+
strictEffectApproach?: true;
|
|
12
13
|
openapi?: boolean | { path?: string };
|
|
13
14
|
dataPlatform?: {
|
|
14
15
|
enabled?: boolean;
|
|
@@ -58,7 +59,7 @@ import EnableBFFCaution from "@site-docs/components/enable-bff-caution";
|
|
|
58
59
|
## bff.effect.entry
|
|
59
60
|
|
|
60
61
|
- **类型:** `string`
|
|
61
|
-
- **默认值:** `<apiDirectory>/
|
|
62
|
+
- **默认值:** `<apiDirectory>/index`
|
|
62
63
|
|
|
63
64
|
用于指定 Effect HttpApi 运行时入口模块。
|
|
64
65
|
|
|
@@ -67,12 +68,19 @@ export default defineConfig({
|
|
|
67
68
|
bff: {
|
|
68
69
|
runtimeFramework: 'effect',
|
|
69
70
|
effect: {
|
|
70
|
-
entry: './api/
|
|
71
|
+
entry: './api/index',
|
|
71
72
|
},
|
|
72
73
|
},
|
|
73
74
|
});
|
|
74
75
|
```
|
|
75
76
|
|
|
77
|
+
## bff.effect.strictEffectApproach
|
|
78
|
+
|
|
79
|
+
- **类型:** `true`
|
|
80
|
+
- **默认值:** `true`
|
|
81
|
+
|
|
82
|
+
Effect BFF 模块必须导出严格 Effect API surface(`defineEffectBff(...)` 或 `{ api, layer }`),原始 request handler 会被拒绝。生成的 UltraModern 应用会显式写入这个标记。
|
|
83
|
+
|
|
76
84
|
## bff.effect.openapi
|
|
77
85
|
|
|
78
86
|
- **类型:** `boolean | { path?: string }`
|
|
@@ -173,4 +181,4 @@ export default defineConfig({
|
|
|
173
181
|
|
|
174
182
|
`batch.flushIntervalMs` 用于控制生成的 Effect 客户端微批处理窗口;`maxConcurrency` 与 `requestTimeoutMs` 由服务端批处理网关用于内部请求分发。
|
|
175
183
|
|
|
176
|
-
生成的 `
|
|
184
|
+
生成的 `api.client.*` API 只存在于 loader 物化后的 `@api/index` 导入中。直接导入服务端入口(`api/index`)时拿到的是 Effect BFF 定义;其中的 `client` 属性只是占位,并会在访问具体操作时报错。
|
|
@@ -21,8 +21,8 @@ export default defineConfig({
|
|
|
21
21
|
});
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
- 设置为 `'effect'` 时,仅从 `api/
|
|
24
|
+
- 设置为 `'effect'` 时,仅从 `api/index` 运行 BFF(Effect HttpApi 运行时)。
|
|
25
25
|
- 设置为 `'hono'` 时,仅从 `api/lambda/**` 的文件约定处理函数运行 BFF。
|
|
26
26
|
|
|
27
27
|
两种运行时之间没有隐式回退,应用需要显式选择其一。
|
|
28
|
-
生成的 UltraModern
|
|
28
|
+
生成的 UltraModern 应用默认使用严格 Effect API,并设置 `bff.effect.strictEffectApproach: true`。
|
|
@@ -59,9 +59,9 @@ Envelope 校验默认开启,但只有设置 `requireEnvelope` 时才强制要
|
|
|
59
59
|
|
|
60
60
|
## Effect 客户端自动行为
|
|
61
61
|
|
|
62
|
-
使用生成的 Effect 客户端(`@api/
|
|
62
|
+
使用生成的 Effect 客户端(`@api/index`)时,Modern.js 会在同源请求中自动附加序列化的 envelope 请求头(`x-modernjs-data-envelope`)。
|
|
63
63
|
|
|
64
|
-
生成客户端由 loader 物化。请在浏览器或会被客户端打包的代码中通过 `@api/
|
|
64
|
+
生成客户端由 loader 物化。请在浏览器或会被客户端打包的代码中通过 `@api/index` 导入;直接导入服务端入口(`api/index`)时拿到的是 Effect BFF 定义,其中只包含占位的 `client`。
|
|
65
65
|
|
|
66
66
|
Envelope 默认包含:
|
|
67
67
|
|
|
@@ -7,7 +7,7 @@ title: 运行时框架
|
|
|
7
7
|
|
|
8
8
|
Modern.js 目前支持两种 BFF 运行时框架:
|
|
9
9
|
|
|
10
|
-
- `effect`(默认):使用 `api/
|
|
10
|
+
- `effect`(默认):使用 `api/index` 的 [Effect HttpApi](https://effect.website/) 运行时。
|
|
11
11
|
- `hono`:使用 `api/lambda/**` 的文件约定 BFF 处理函数。
|
|
12
12
|
|
|
13
13
|
`effect` 与 `hono` 为严格模式,两者之间不会自动回退。
|
|
@@ -23,6 +23,8 @@ export default defineConfig({
|
|
|
23
23
|
bff: {
|
|
24
24
|
runtimeFramework: 'effect',
|
|
25
25
|
effect: {
|
|
26
|
+
entry: './api/index',
|
|
27
|
+
strictEffectApproach: true,
|
|
26
28
|
openapi: {
|
|
27
29
|
path: '/openapi.json',
|
|
28
30
|
},
|
|
@@ -33,7 +35,7 @@ export default defineConfig({
|
|
|
33
35
|
|
|
34
36
|
先在共享目录中定义 Effect HttpApi 契约(前后端共享类型):
|
|
35
37
|
|
|
36
|
-
```ts title="shared/
|
|
38
|
+
```ts title="shared/api.ts"
|
|
37
39
|
import {
|
|
38
40
|
HttpApi,
|
|
39
41
|
HttpApiEndpoint,
|
|
@@ -41,18 +43,18 @@ import {
|
|
|
41
43
|
Schema,
|
|
42
44
|
} from '@modern-js/plugin-bff/effect-client';
|
|
43
45
|
|
|
44
|
-
export const
|
|
46
|
+
export const bffApi = HttpApi.make('MyApi').add(
|
|
45
47
|
HttpApiGroup.make('hello').add(
|
|
46
|
-
HttpApiEndpoint.get('ping', '/
|
|
48
|
+
HttpApiEndpoint.get('ping', '/ping', {
|
|
47
49
|
success: Schema.String,
|
|
48
50
|
}),
|
|
49
51
|
),
|
|
50
52
|
);
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
然后在 `api/
|
|
55
|
+
然后在 `api/index.ts` 中实现 Effect BFF 入口:
|
|
54
56
|
|
|
55
|
-
```ts title="api/
|
|
57
|
+
```ts title="api/index.ts"
|
|
56
58
|
import {
|
|
57
59
|
Schema,
|
|
58
60
|
Effect,
|
|
@@ -61,7 +63,7 @@ import {
|
|
|
61
63
|
Layer,
|
|
62
64
|
ServiceMap,
|
|
63
65
|
} from '@modern-js/plugin-bff/effect-server';
|
|
64
|
-
import {
|
|
66
|
+
import { bffApi } from '../shared/api';
|
|
65
67
|
|
|
66
68
|
class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableError>()(
|
|
67
69
|
'GreetingUnavailableError',
|
|
@@ -87,7 +89,7 @@ class GreetingService extends ServiceMap.Service<GreetingService>()('GreetingSer
|
|
|
87
89
|
static readonly layer = Layer.effect(this, this.make);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
const group = HttpApiBuilder.group(
|
|
92
|
+
const group = HttpApiBuilder.group(bffApi, 'hello', handlers =>
|
|
91
93
|
handlers.handle('ping', () =>
|
|
92
94
|
GreetingService.use(service => service.hello()).pipe(
|
|
93
95
|
Effect.catchTag('GreetingUnavailableError', error =>
|
|
@@ -97,23 +99,23 @@ const group = HttpApiBuilder.group(bffEffectApi, 'hello', handlers =>
|
|
|
97
99
|
),
|
|
98
100
|
);
|
|
99
101
|
|
|
100
|
-
const layer = HttpApiBuilder.layer(
|
|
102
|
+
const layer = HttpApiBuilder.layer(bffApi).pipe(
|
|
101
103
|
Layer.provide(group),
|
|
102
104
|
Layer.provide(GreetingService.layer),
|
|
103
105
|
);
|
|
104
106
|
|
|
105
|
-
export default defineEffectBff({ api:
|
|
107
|
+
export default defineEffectBff({ api: bffApi, layer });
|
|
106
108
|
```
|
|
107
109
|
|
|
108
|
-
在浏览器代码中通过 `@api/
|
|
110
|
+
在浏览器代码中通过 `@api/index` 调用接口:
|
|
109
111
|
|
|
110
112
|
```ts title="src/routes/page.tsx"
|
|
111
|
-
import
|
|
113
|
+
import api from '@api/index';
|
|
112
114
|
|
|
113
|
-
const response = await
|
|
115
|
+
const response = await api.client.hello.ping({});
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
`
|
|
118
|
+
`api.client.*` 由 BFF loader 针对 `@api/index` 导入物化生成。不要直接导入 `api/index` 并期望在服务端代码、脚本或测试中运行 `client`;直接导入入口时拿到的是服务端运行时定义,其中的 `client` 只是类型占位。
|
|
117
119
|
|
|
118
120
|
import Hono from '@site-docs/components/hono';
|
|
119
121
|
|
|
@@ -33,7 +33,7 @@ UltraModern.js 的增强能力是新 SuperApp 的默认产品面。兼容分支
|
|
|
33
33
|
| 构建诊断能力 | RsDoctor 多为显式开启 | 新增一等的 `performance.rsdoctor` 配置项(按需开启;早期的默认开启行为与诊断契约产物已回退) |
|
|
34
34
|
| 输出与静态资源回源 | 预压缩策略通常由业务自定义 | 默认开启 `output.precompress`,并按 `Accept-Encoding` 协商 `.br` / `.gz` 回源 |
|
|
35
35
|
| BFF 运行时与契约 | 提供标准 BFF 运行时与客户端生成能力 | 增加 `requestId` 维度隔离、初始化 fail-fast 校验与操作/追踪关联 Header |
|
|
36
|
-
| BFF 运行时选型 | Modern.js 3.0 基线仅提供 Hono 运行时路径(无内建 Effect 运行时) | 将 Effect
|
|
36
|
+
| BFF 运行时选型 | Modern.js 3.0 基线仅提供 Hono 运行时路径(无内建 Effect 运行时) | 将 Effect HttpApi 设为默认 API surface,入口固定为 `api/index.ts`,共享契约固定为 `shared/api.ts`,并通过 `strictEffectApproach` 拒绝原始 request handler |
|
|
37
37
|
| Telemetry 标准化 | 可观测链路通常由业务侧自行拼装 | 增加框架级 telemetry 管线,内置 OTLP/VictoriaMetrics,支持脱敏、批处理与背压 |
|
|
38
38
|
| 应用级 MF SSR 协议 | 没有以 super-app 为重点的应用级稳定性契约开关 | 增加 `server.ssr.moduleFederationAppSSR` 配置/环境变量握手,并补齐集成级回归保障 |
|
|
39
39
|
| MF vertical 加载可靠性 | 重试/降级策略通常由各业务单独实现 | 增加 timeout/network/contract-error 的确定性可靠性矩阵与分布式 OTEL 连续性断言 |
|
|
@@ -46,7 +46,7 @@ UltraModern.js 的增强能力是新 SuperApp 的默认产品面。兼容分支
|
|
|
46
46
|
|
|
47
47
|
- 不再把这个 fork 隐藏在旧 Modern.js 品牌后面。
|
|
48
48
|
- 当通用 Modern.js 默认值与 SuperApp 可靠性冲突时,不优先优化通用默认值。
|
|
49
|
-
-
|
|
49
|
+
- 生成的 UltraModern API 只走严格 Effect surface;原始 request handler 和 Hono/file-function API 是显式非默认路径。
|
|
50
50
|
- 除非稳定性硬需求,否则避免引入破坏性 API 变更。
|
|
51
51
|
|
|
52
52
|
## 迁移指南
|
|
@@ -54,7 +54,7 @@ UltraModern.js 的增强能力是新 SuperApp 的默认产品面。兼容分支
|
|
|
54
54
|
对于已经在使用 Modern.js 3.0 或旧版 BleedingDev UltraModern scaffold 的团队,迁移路径是“兼容感知”:保留能降低风险的部分,逐步向 MV-first / TanStack-first / Effect-first 默认方向收敛,并把 UltraModern.js 3.0 作为独立框架采用。
|
|
55
55
|
|
|
56
56
|
1. 既有 React Router 应用可以继续按现状运行。TanStack Router 是新脚手架与增量迁移的优先路径,但 React Router 兼容分支仍然保留,团队可以按自己的节奏迁移。
|
|
57
|
-
2. 新建或迁移中的 BFF
|
|
57
|
+
2. 新建或迁移中的 BFF 能力使用 `bff.runtimeFramework: 'effect'`、`bff.effect.entry: './api/index'` 和 `bff.effect.strictEffectApproach: true`。接口先改 `shared/api.ts` 的 `HttpApi` 契约,再在 `api/index.ts` 用 `defineEffectBff(...)` / `HttpApiBuilder` 实现。
|
|
58
58
|
3. 将基线契约视为渐进式默认值,而不是一次性强制切换。`MODERN_BASELINE_ENABLE_MF_SSR`、`MODERN_BASELINE_ENABLE_BFF_REQUEST_ID` 和 `MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS` 已经提供了按应用、按环境关闭的能力,方便在收敛到目标栈之前逐步过渡。
|
|
59
59
|
4. 这套公开预设现在已经附带显式的发布 / 认证 gate。生成 workspace 会自带 `.github/workflows/ultramodern-workspace-gates.yml`,因此 `pnpm check` 与 `pnpm build` 从第一天开始就是本地接入契约的一部分;CI 会以并行矩阵运行这些基础 gate。
|
|
60
60
|
|
|
@@ -70,9 +70,40 @@ mise exec -- pnpm check
|
|
|
70
70
|
mise exec -- pnpm build
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
严格 Effect API 迁移不兼容 `3.4.0-ultramodern.19` 或更早的包 cohort。
|
|
74
|
+
这些包还没有 `strictEffectApproach` 配置类型,也没有直接 `api/index.ts`
|
|
75
|
+
的生成器和检查。新的 BleedingDev cohort 发布前,迁移校验应使用本地
|
|
76
|
+
Modern.js workspace;发布后,用 `--ultramodern-package-version` 固定该新
|
|
77
|
+
cohort。
|
|
78
|
+
|
|
79
|
+
迁移已经带有 Effect API 文件的生成 workspace 时,明确移动文件,不保留兼容别名:
|
|
80
|
+
|
|
81
|
+
1. 将每个 vertical 服务端入口从 `verticals/<id>/api/effect/index.ts` 移到
|
|
82
|
+
`verticals/<id>/api/index.ts`。
|
|
83
|
+
2. 将每个共享 API 契约从 `verticals/<id>/shared/effect/api.ts` 移到
|
|
84
|
+
`verticals/<id>/shared/api.ts`。
|
|
85
|
+
3. 将生成客户端从 `verticals/<id>/src/effect/*-client.ts` 移到
|
|
86
|
+
`verticals/<id>/src/api/*-client.ts`。
|
|
87
|
+
4. 将 shell API 聚合从 `apps/shell-super-app/src/effect/*` 移到
|
|
88
|
+
`apps/shell-super-app/src/api/*`。
|
|
89
|
+
5. 更新 imports 和 package exports,使用 `./api`、`./api/client` 与
|
|
90
|
+
`@<workspace>/<vertical>/api/client`;删除 `./effect/client`、
|
|
91
|
+
`./shared/effect/api` 和 shared Effect API 包。
|
|
92
|
+
6. 更新每个 vertical 的 `modern.config.ts`,设置
|
|
93
|
+
`bff.effect.entry: './api/index'` 和
|
|
94
|
+
`bff.effect.strictEffectApproach: true`。
|
|
95
|
+
7. 更新 topology,让 API metadata 直接位于 `api` 下,并设置
|
|
96
|
+
`api.bff.strictEffectApproach: true`;不要保留 `api.effect`。
|
|
97
|
+
8. 运行 `pnpm api:check`、`scripts/validate-ultramodern-workspace.mjs`、
|
|
98
|
+
`pnpm check` 和 `pnpm build`。
|
|
99
|
+
|
|
100
|
+
如果迁移失败,应修复归属的生成契约、topology、package exports 或 API 模块。
|
|
101
|
+
不要添加 app 级 alias、原始 request handler、手写 `Response`、本地类型断言或包
|
|
102
|
+
shim 来让旧布局通过。
|
|
103
|
+
|
|
73
104
|
每个仓库只使用一种 package-source 策略。已发布的 BleedingDev create 包默认使用
|
|
74
105
|
`--ultramodern-package-source=install`,并把精确 cohort 记录到
|
|
75
|
-
`.modernjs/ultramodern
|
|
106
|
+
`.modernjs/ultramodern.json`。`--workspace` 只用于本地 monorepo
|
|
76
107
|
联调未发布包。发布证明和 CI 如果需要证明某个已发布框架版本,应使用
|
|
77
108
|
`--ultramodern-package-version` 固定精确 cohort。
|
|
78
109
|
|
|
@@ -82,7 +113,7 @@ Federation shim 或自定义 server wrapper。框架 BFF compiler 会把服务
|
|
|
82
113
|
CommonJS,同时生成的 app package 继续为 Module Federation DTS 保留稳定的
|
|
83
114
|
`typescript`,并使用固定的 `@typescript/native-preview` 工具链执行 TS-Go 检查。
|
|
84
115
|
|
|
85
|
-
安装新 cohort 后,先运行生成的
|
|
116
|
+
安装新 cohort 后,先运行生成的 `pnpm api:check` 与
|
|
86
117
|
`scripts/validate-ultramodern-workspace.mjs` 契约校验,再接受人工改动。topology、
|
|
87
118
|
ownership、package-source、本地 overlay、生成契约、Tailwind prefix 或 Module
|
|
88
119
|
Federation 冲突,都应在对应归属文件中修复,不要手写补丁覆盖生成结果。
|
|
@@ -215,7 +246,7 @@ JSON-LD 是可选的路由 metadata,不会自动推断。私有路由和不可
|
|
|
215
246
|
JSON-LD。公开路由 owner 可以在本地化路径旁显式添加 `jsonLd`,并使用生成的
|
|
216
247
|
`src/routes/ultramodern-jsonld.ts` helper 编写常见 schema.org 结构。
|
|
217
248
|
|
|
218
|
-
生成契约会在 `.modernjs/ultramodern
|
|
249
|
+
生成契约会在 `.modernjs/ultramodern.json` 中写入
|
|
219
250
|
`cssFederation`:
|
|
220
251
|
|
|
221
252
|
- `packages/shared-design-tokens` 拥有共享 token layer,并导出 `./tokens.css`。
|
package/package.json
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"modern.js",
|
|
20
20
|
"ultramodern.js"
|
|
21
21
|
],
|
|
22
|
-
"version": "3.4.0-ultramodern.
|
|
22
|
+
"version": "3.4.0-ultramodern.20",
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"registry": "https://registry.npmjs.org/",
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"mermaid": "^11.15.0",
|
|
29
|
-
"@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.4.0-ultramodern.
|
|
29
|
+
"@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.4.0-ultramodern.20"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@rsbuild/plugin-sass": "2.0.0",
|