@bleedingdev/modern-js-main-doc 3.5.0-ultramodern.0 → 3.5.0-ultramodern.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -45,6 +45,13 @@ import EnableBFFCaution from "@site-docs-en/components/enable-bff-caution";
|
|
|
45
45
|
|
|
46
46
|
`bff.effect` is only effective when `bff.runtimeFramework` is set to `'effect'`.
|
|
47
47
|
|
|
48
|
+
Generated UltraModern workspaces use this runtime as the only generated HTTP API
|
|
49
|
+
path. The API contract lives at `shared/api.ts`, the server runtime lives at
|
|
50
|
+
`api/index.ts`, clients live under `src/api/*-client.ts`, and generated checks
|
|
51
|
+
reject `api/effect`, `api/lambda`, `shared/effect`, `src/effect`, Hono server
|
|
52
|
+
imports, raw request handlers, manual request parsing, and manual `Response`
|
|
53
|
+
construction in API modules.
|
|
54
|
+
|
|
48
55
|
For Effect v4, TypeScript should use export-map aware module resolution.
|
|
49
56
|
|
|
50
57
|
```json title="tsconfig.json"
|
|
@@ -186,3 +193,97 @@ export default defineConfig({
|
|
|
186
193
|
`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.
|
|
187
194
|
|
|
188
195
|
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.
|
|
196
|
+
|
|
197
|
+
## Effect cohort
|
|
198
|
+
|
|
199
|
+
UltraModern-generated workspaces pin the framework-compatible Effect cohort
|
|
200
|
+
through `pnpm-workspace.yaml` overrides. For `3.5.0-ultramodern.1`, generated
|
|
201
|
+
apps use:
|
|
202
|
+
|
|
203
|
+
```yaml
|
|
204
|
+
overrides:
|
|
205
|
+
'@effect/vitest': 4.0.0-beta.89
|
|
206
|
+
effect: 4.0.0-beta.89
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Do not add a different direct `effect` version in an app package. A mismatched
|
|
210
|
+
Effect beta can fail while building layers or HTTP middleware because runtime
|
|
211
|
+
services come from different package instances.
|
|
212
|
+
|
|
213
|
+
## Contract tests
|
|
214
|
+
|
|
215
|
+
Strict Effect APIs should test the declared `HttpApi` contract, not a raw
|
|
216
|
+
request handler. Edge-compatible tests can use the framework helper:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
|
|
220
|
+
import apiModule from '../api/index';
|
|
221
|
+
|
|
222
|
+
const testApi = await createEffectBffTestHandler({
|
|
223
|
+
module: apiModule,
|
|
224
|
+
prefix: '/api',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const response = await testApi.handler(new Request('http://localhost/api/ping'));
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
If you manually compose an Effect web handler in a low-level proof, provide the
|
|
231
|
+
platform services explicitly:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import {
|
|
235
|
+
HttpApiBuilder,
|
|
236
|
+
HttpRouter,
|
|
237
|
+
HttpServer,
|
|
238
|
+
Layer,
|
|
239
|
+
} from '@modern-js/plugin-bff/effect-server';
|
|
240
|
+
|
|
241
|
+
const handler = HttpRouter.toWebHandler(
|
|
242
|
+
HttpApiBuilder.layer(api).pipe(
|
|
243
|
+
Layer.provide(apiGroupLayer),
|
|
244
|
+
Layer.provide(HttpServer.layerServices),
|
|
245
|
+
),
|
|
246
|
+
).handler;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Prefer the generated helper unless the test needs to inspect the low-level
|
|
250
|
+
Effect router composition.
|
|
251
|
+
|
|
252
|
+
## Dynamic CORS
|
|
253
|
+
|
|
254
|
+
For dynamic origin predicates, use Effect HTTP middleware as a layer. In the
|
|
255
|
+
current Effect v4 beta cohort, `HttpRouter.middleware(...)` returns a `Layer`
|
|
256
|
+
directly:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import {
|
|
260
|
+
Effect,
|
|
261
|
+
HttpApiBuilder,
|
|
262
|
+
HttpMiddleware,
|
|
263
|
+
HttpRouter,
|
|
264
|
+
Layer,
|
|
265
|
+
} from '@modern-js/plugin-bff/effect-server';
|
|
266
|
+
|
|
267
|
+
const corsLayer = HttpRouter.middleware(
|
|
268
|
+
Effect.succeed(
|
|
269
|
+
HttpMiddleware.cors({
|
|
270
|
+
allowedOrigins: origin =>
|
|
271
|
+
origin.endsWith('.example.com') ? origin : undefined,
|
|
272
|
+
}),
|
|
273
|
+
),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const layer = HttpApiBuilder.layer(api).pipe(
|
|
277
|
+
Layer.provide(apiGroupLayer),
|
|
278
|
+
Layer.provide(corsLayer),
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Do not read a `.layer` property from `HttpRouter.middleware(...)`.
|
|
283
|
+
|
|
284
|
+
## Other transports
|
|
285
|
+
|
|
286
|
+
`strictEffectApproach` governs generated HTTP API modules. Effect RPC,
|
|
287
|
+
WebSockets, and other transports remain valid when they are modeled as explicit
|
|
288
|
+
transport surfaces with typed Effect programs. They do not make raw HTTP
|
|
289
|
+
request handlers valid inside generated UltraModern API modules.
|
|
@@ -78,12 +78,33 @@ mise exec -- pnpm check
|
|
|
78
78
|
mise exec -- pnpm build
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
Strict generated API migration requires `3.5.0-ultramodern.
|
|
81
|
+
Strict generated API migration requires `3.5.0-ultramodern.1` or newer.
|
|
82
82
|
`3.4.0-ultramodern.20` and earlier cohorts do not include this full direct
|
|
83
|
-
`api/index.ts` generator, generated `.mts` checks,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`3.5.0-ultramodern.
|
|
83
|
+
`api/index.ts` generator, generated `.mts` checks, strict Oxlint boundary rule
|
|
84
|
+
set, Effect cohort overrides, and strict Effect migration command. Agents that
|
|
85
|
+
cannot install that BleedingDev cohort yet should use the local Modern.js
|
|
86
|
+
workspace for migration validation; otherwise pin `3.5.0-ultramodern.1` or
|
|
87
|
+
newer with `--ultramodern-package-version`.
|
|
88
|
+
|
|
89
|
+
Before hand-editing package aliases or generated metadata, run the framework
|
|
90
|
+
migration command from the target workspace:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.1 ultramodern \
|
|
94
|
+
migrate-strict-effect --version 3.5.0-ultramodern.1
|
|
95
|
+
pnpm api:check
|
|
96
|
+
pnpm contract:check
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The command updates `.modernjs/ultramodern.json`, root
|
|
100
|
+
`modernjs.packageSource`, generated Modern package aliases, direct topology API
|
|
101
|
+
metadata, and the pnpm lockfile. Remaining failures are source migration work:
|
|
102
|
+
move code to `shared/api.ts`, `api/index.ts`, and `src/api/*-client.ts`, then
|
|
103
|
+
delete `api/effect`, `api/lambda`, `shared/effect`, and `src/effect`.
|
|
104
|
+
|
|
105
|
+
Generated strict Effect workspaces pin the compatible Effect cohort with pnpm
|
|
106
|
+
overrides: `effect@4.0.0-beta.89` and `@effect/vitest@4.0.0-beta.89`. Do not
|
|
107
|
+
add app-local direct Effect versions that disagree with those overrides.
|
|
87
108
|
|
|
88
109
|
Gradual migration means old, unmigrated Modern.js apps can keep their existing
|
|
89
110
|
runtime until they are converted. Once a surface is generated or migrated as
|
|
@@ -122,6 +143,17 @@ Effect RPC, WebSockets, and other transports should be added as explicit
|
|
|
122
143
|
transport surfaces when needed. They do not make raw request handlers valid
|
|
123
144
|
inside generated HTTP API modules.
|
|
124
145
|
|
|
146
|
+
Strict API tests should exercise the `HttpApi` contract. Use
|
|
147
|
+
`createEffectBffTestHandler` from `@modern-js/plugin-bff/effect-edge` for
|
|
148
|
+
edge-compatible proof tests; if you manually compose a web handler, provide
|
|
149
|
+
`HttpServer.layerServices` beside your API group layer before calling
|
|
150
|
+
`HttpRouter.toWebHandler`.
|
|
151
|
+
|
|
152
|
+
For dynamic origin CORS predicates inside strict Effect HTTP APIs, prefer
|
|
153
|
+
`HttpRouter.middleware(Effect.succeed(HttpMiddleware.cors(...)))`.
|
|
154
|
+
`HttpRouter.middleware(...)` returns a `Layer` directly in the pinned Effect
|
|
155
|
+
cohort; do not read a `.layer` property from it.
|
|
156
|
+
|
|
125
157
|
Use one package-source strategy per repo. The published BleedingDev create
|
|
126
158
|
package defaults to `--ultramodern-package-source=install` and records the
|
|
127
159
|
exact cohort in `.modernjs/ultramodern.json`. Keep `--workspace` only for local
|
|
@@ -148,6 +180,13 @@ manual edits. Fix topology, ownership, package-source, local overlay, generated
|
|
|
148
180
|
contract, Tailwind prefix, or Module Federation conflicts in the owning files
|
|
149
181
|
instead of patching generated output by hand.
|
|
150
182
|
|
|
183
|
+
Cloudflare D1 bindings are first-class on `deploy.worker.d1Databases`; use that
|
|
184
|
+
config instead of app-local postprocessing when a generated app owns D1
|
|
185
|
+
migrations. Cloudflare public output excludes server-only `api` and `shared`
|
|
186
|
+
directories by default, and generated Modern/Rspack caches are isolated per app
|
|
187
|
+
and build target so local `build` and `cloudflare:build` runs do not share the
|
|
188
|
+
same Rspack cache directory.
|
|
189
|
+
|
|
151
190
|
## Human Workflow
|
|
152
191
|
|
|
153
192
|
The public BleedingDev create package has one supported generated product. The
|
|
@@ -45,6 +45,12 @@ import EnableBFFCaution from "@site-docs/components/enable-bff-caution";
|
|
|
45
45
|
|
|
46
46
|
仅当 `bff.runtimeFramework` 设置为 `'effect'` 时,`bff.effect` 才会生效。
|
|
47
47
|
|
|
48
|
+
生成的 UltraModern workspace 只把这个运行时作为生成 HTTP API 路径。API 契约固定在
|
|
49
|
+
`shared/api.ts`,服务端运行时固定在 `api/index.ts`,客户端固定在
|
|
50
|
+
`src/api/*-client.ts`。生成检查会拒绝 `api/effect`、`api/lambda`、
|
|
51
|
+
`shared/effect`、`src/effect`、Hono server import、原始 request handler、手写
|
|
52
|
+
request parsing,以及 API 模块里的手写 `Response`。
|
|
53
|
+
|
|
48
54
|
对于 Effect v4,TypeScript 需要使用支持 `exports` 映射的模块解析方式。
|
|
49
55
|
|
|
50
56
|
```json title="tsconfig.json"
|
|
@@ -185,3 +191,92 @@ export default defineConfig({
|
|
|
185
191
|
`batch.flushIntervalMs` 用于控制生成的 Effect 客户端微批处理窗口;`maxConcurrency` 与 `requestTimeoutMs` 由服务端批处理网关用于内部请求分发。
|
|
186
192
|
|
|
187
193
|
生成的 `api.client.*` API 只存在于 loader 物化后的 `@api/index` 导入中。直接导入服务端入口(`api/index`)时拿到的是 Effect BFF 定义;其中的 `client` 属性只是占位,并会在访问具体操作时报错。
|
|
194
|
+
|
|
195
|
+
## Effect 版本组
|
|
196
|
+
|
|
197
|
+
UltraModern 生成的 workspace 会通过 `pnpm-workspace.yaml` overrides 锁定与框架兼容的
|
|
198
|
+
Effect 版本组。`3.5.0-ultramodern.1` 使用:
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
overrides:
|
|
202
|
+
'@effect/vitest': 4.0.0-beta.89
|
|
203
|
+
effect: 4.0.0-beta.89
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
不要在应用包里添加不同版本的直接 `effect` 依赖。Effect beta 不一致时,Layer 或 HTTP
|
|
207
|
+
middleware 构建可能因为运行时 service 来自不同包实例而失败。
|
|
208
|
+
|
|
209
|
+
## 契约测试
|
|
210
|
+
|
|
211
|
+
严格 Effect API 测试应当测试声明的 `HttpApi` 契约,而不是原始 request handler。
|
|
212
|
+
Edge 兼容测试可以使用框架 helper:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
|
|
216
|
+
import apiModule from '../api/index';
|
|
217
|
+
|
|
218
|
+
const testApi = await createEffectBffTestHandler({
|
|
219
|
+
module: apiModule,
|
|
220
|
+
prefix: '/api',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const response = await testApi.handler(new Request('http://localhost/api/ping'));
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
如果低层 proof 必须手动组合 web handler,需要在调用 `HttpRouter.toWebHandler` 前提供
|
|
227
|
+
`HttpServer.layerServices`:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import {
|
|
231
|
+
HttpApiBuilder,
|
|
232
|
+
HttpRouter,
|
|
233
|
+
HttpServer,
|
|
234
|
+
Layer,
|
|
235
|
+
} from '@modern-js/plugin-bff/effect-server';
|
|
236
|
+
|
|
237
|
+
const handler = HttpRouter.toWebHandler(
|
|
238
|
+
HttpApiBuilder.layer(api).pipe(
|
|
239
|
+
Layer.provide(apiGroupLayer),
|
|
240
|
+
Layer.provide(HttpServer.layerServices),
|
|
241
|
+
),
|
|
242
|
+
).handler;
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
优先使用生成 helper,除非测试必须检查底层 Effect router 组合。
|
|
246
|
+
|
|
247
|
+
## 动态 CORS
|
|
248
|
+
|
|
249
|
+
需要动态 origin predicate 时,使用 Effect HTTP middleware 作为 Layer。在当前锁定的
|
|
250
|
+
Effect v4 beta 版本组中,`HttpRouter.middleware(...)` 直接返回 `Layer`:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import {
|
|
254
|
+
Effect,
|
|
255
|
+
HttpApiBuilder,
|
|
256
|
+
HttpMiddleware,
|
|
257
|
+
HttpRouter,
|
|
258
|
+
Layer,
|
|
259
|
+
} from '@modern-js/plugin-bff/effect-server';
|
|
260
|
+
|
|
261
|
+
const corsLayer = HttpRouter.middleware(
|
|
262
|
+
Effect.succeed(
|
|
263
|
+
HttpMiddleware.cors({
|
|
264
|
+
allowedOrigins: origin =>
|
|
265
|
+
origin.endsWith('.example.com') ? origin : undefined,
|
|
266
|
+
}),
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const layer = HttpApiBuilder.layer(api).pipe(
|
|
271
|
+
Layer.provide(apiGroupLayer),
|
|
272
|
+
Layer.provide(corsLayer),
|
|
273
|
+
);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
不要从 `HttpRouter.middleware(...)` 读取 `.layer` 属性。
|
|
277
|
+
|
|
278
|
+
## 其他传输
|
|
279
|
+
|
|
280
|
+
`strictEffectApproach` 约束生成的 HTTP API 模块。Effect RPC、WebSocket 和其他传输在
|
|
281
|
+
建模为显式传输 surface、并使用类型化 Effect 程序时仍然有效。它们不让生成的
|
|
282
|
+
UltraModern API 模块中的原始 HTTP request handler 变得有效。
|
|
@@ -71,11 +71,30 @@ mise exec -- pnpm check
|
|
|
71
71
|
mise exec -- pnpm build
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
严格生成 API 迁移要求 `3.5.0-ultramodern.
|
|
74
|
+
严格生成 API 迁移要求 `3.5.0-ultramodern.1` 或更新版本。
|
|
75
75
|
`3.4.0-ultramodern.20` 及更早 cohort 还没有这套完整的直接
|
|
76
|
-
`api/index.ts` 生成器、生成的 `.mts`
|
|
77
|
-
BleedingDev cohort 的 agent 应使用本地
|
|
78
|
-
`--ultramodern-package-version` 固定
|
|
76
|
+
`api/index.ts` 生成器、生成的 `.mts` 检查、严格 Oxlint 边界规则、Effect 版本组
|
|
77
|
+
overrides 和严格 Effect 迁移命令。还不能安装该 BleedingDev cohort 的 agent 应使用本地
|
|
78
|
+
Modern.js workspace 做迁移校验;否则用 `--ultramodern-package-version` 固定
|
|
79
|
+
`3.5.0-ultramodern.1` 或更新版本。
|
|
80
|
+
|
|
81
|
+
手写 package alias 或生成 metadata 之前,先在目标 workspace 运行框架迁移命令:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.1 ultramodern \
|
|
85
|
+
migrate-strict-effect --version 3.5.0-ultramodern.1
|
|
86
|
+
pnpm api:check
|
|
87
|
+
pnpm contract:check
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
该命令会更新 `.modernjs/ultramodern.json`、根 `modernjs.packageSource`、生成的
|
|
91
|
+
Modern package alias、直接 topology API metadata 和 pnpm lockfile。剩余失败就是源码迁移:
|
|
92
|
+
把代码移到 `shared/api.ts`、`api/index.ts` 和 `src/api/*-client.ts`,再删除
|
|
93
|
+
`api/effect`、`api/lambda`、`shared/effect` 和 `src/effect`。
|
|
94
|
+
|
|
95
|
+
严格 Effect 生成 workspace 会通过 pnpm overrides 固定兼容版本组:
|
|
96
|
+
`effect@4.0.0-beta.89` 和 `@effect/vitest@4.0.0-beta.89`。不要添加与这些
|
|
97
|
+
overrides 冲突的 app 本地直接 Effect 版本。
|
|
79
98
|
|
|
80
99
|
渐进迁移意味着尚未迁移的旧 Modern.js app 可以保留现有 runtime,直到被转换为
|
|
81
100
|
UltraModern。一个 surface 一旦生成为或迁移为 UltraModern HTTP API,就只能使用
|
|
@@ -110,6 +129,15 @@ shim 来让旧布局通过。
|
|
|
110
129
|
Effect RPC、WebSockets 和其他传输方式应在需要时作为显式 transport surface 增加。
|
|
111
130
|
它们不能作为在生成 HTTP API 模块中重新引入原始 request handler 的理由。
|
|
112
131
|
|
|
132
|
+
严格 API 测试应执行 `HttpApi` 契约。Edge 兼容 proof 测试使用
|
|
133
|
+
`@modern-js/plugin-bff/effect-edge` 的 `createEffectBffTestHandler`;如果必须手动组合
|
|
134
|
+
web handler,在调用 `HttpRouter.toWebHandler` 前要把 `HttpServer.layerServices` 和
|
|
135
|
+
API group layer 一起提供。
|
|
136
|
+
|
|
137
|
+
动态 origin CORS predicate 使用
|
|
138
|
+
`HttpRouter.middleware(Effect.succeed(HttpMiddleware.cors(...)))`。在固定的 Effect
|
|
139
|
+
版本组中,`HttpRouter.middleware(...)` 直接返回 `Layer`;不要读取 `.layer` 属性。
|
|
140
|
+
|
|
113
141
|
每个仓库只使用一种 package-source 策略。已发布的 BleedingDev create 包默认使用
|
|
114
142
|
`--ultramodern-package-source=install`,并把精确 cohort 记录到
|
|
115
143
|
`.modernjs/ultramodern.json`。`--workspace` 只用于本地 monorepo
|
|
@@ -127,6 +155,11 @@ CommonJS,同时生成的 app package 继续为 Module Federation DTS 保留稳
|
|
|
127
155
|
ownership、package-source、本地 overlay、生成契约、Tailwind prefix 或 Module
|
|
128
156
|
Federation 冲突,都应在对应归属文件中修复,不要手写补丁覆盖生成结果。
|
|
129
157
|
|
|
158
|
+
Cloudflare D1 绑定使用一等配置 `deploy.worker.d1Databases`;生成 app 拥有 D1
|
|
159
|
+
migration 时不要再用 app 本地 postprocess。Cloudflare public output 默认排除服务端
|
|
160
|
+
`api` 和 `shared` 目录,并且生成的 Modern/Rspack cache 会按 app 与构建目标隔离,避免
|
|
161
|
+
本地 `build` 与 `cloudflare:build` 共享同一个 Rspack cache 目录。
|
|
162
|
+
|
|
130
163
|
## 人类工作流
|
|
131
164
|
|
|
132
165
|
公开的 BleedingDev create 包只有一个受支持的生成产品。默认命令会创建一个
|
package/package.json
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"modern.js",
|
|
20
20
|
"ultramodern.js"
|
|
21
21
|
],
|
|
22
|
-
"version": "3.5.0-ultramodern.
|
|
22
|
+
"version": "3.5.0-ultramodern.1",
|
|
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.5.0-ultramodern.
|
|
29
|
+
"@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.5.0-ultramodern.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@rsbuild/plugin-sass": "2.0.0",
|