@bleedingdev/modern-js-main-doc 3.5.0-ultramodern.0 → 3.5.0-ultramodern.10

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,109 @@ 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.10`, generated
201
+ apps use:
202
+
203
+ ```yaml
204
+ minimumReleaseAgeExclude:
205
+ - 'effect@4.0.0-beta.91'
206
+ - '@effect/opentelemetry@4.0.0-beta.91'
207
+
208
+ trustPolicyExclude:
209
+ - 'effect@4.0.0-beta.91'
210
+ - '@effect/opentelemetry@4.0.0-beta.91'
211
+
212
+ overrides:
213
+ '@effect/vitest': 4.0.0-beta.91
214
+ effect: 4.0.0-beta.91
215
+ ```
216
+
217
+ Do not add a different direct `effect` version in an app package. A mismatched
218
+ Effect beta can fail while building layers or HTTP middleware because runtime
219
+ services come from different package instances. Keep the generated
220
+ policy exclusion entries with the same Effect cohort; they handle the
221
+ framework-selected Effect beta versions before the 24-hour maturity window and
222
+ the `effect`/`@effect/opentelemetry` trusted-publisher to provenance metadata
223
+ transition without weakening UltraModern API architecture checks.
224
+
225
+ ## Contract tests
226
+
227
+ Strict Effect APIs should test the declared `HttpApi` contract, not a raw
228
+ request handler. Edge-compatible tests can use the framework helper:
229
+
230
+ ```ts
231
+ import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
232
+ import apiModule from '../api/index';
233
+
234
+ const testApi = await createEffectBffTestHandler({
235
+ module: apiModule,
236
+ prefix: '/api',
237
+ });
238
+
239
+ const response = await testApi.handler(new Request('http://localhost/api/ping'));
240
+ ```
241
+
242
+ If you manually compose an Effect web handler in a low-level proof, provide the
243
+ platform services explicitly:
244
+
245
+ ```ts
246
+ import {
247
+ HttpApiBuilder,
248
+ HttpRouter,
249
+ HttpServer,
250
+ Layer,
251
+ } from '@modern-js/plugin-bff/effect-server';
252
+
253
+ const handler = HttpRouter.toWebHandler(
254
+ HttpApiBuilder.layer(api).pipe(
255
+ Layer.provide(apiGroupLayer),
256
+ Layer.provide(HttpServer.layerServices),
257
+ ),
258
+ ).handler;
259
+ ```
260
+
261
+ Prefer the generated helper unless the test needs to inspect the low-level
262
+ Effect router composition.
263
+
264
+ ## Dynamic CORS
265
+
266
+ For dynamic origin predicates, use Effect HTTP middleware as a layer. In the
267
+ current Effect v4 beta cohort, `HttpRouter.middleware(...)` returns a `Layer`
268
+ directly:
269
+
270
+ ```ts
271
+ import {
272
+ Effect,
273
+ HttpApiBuilder,
274
+ HttpMiddleware,
275
+ HttpRouter,
276
+ Layer,
277
+ } from '@modern-js/plugin-bff/effect-server';
278
+
279
+ const corsLayer = HttpRouter.middleware(
280
+ Effect.succeed(
281
+ HttpMiddleware.cors({
282
+ allowedOrigins: origin =>
283
+ origin.endsWith('.example.com') ? origin : undefined,
284
+ }),
285
+ ),
286
+ );
287
+
288
+ const layer = HttpApiBuilder.layer(api).pipe(
289
+ Layer.provide(apiGroupLayer),
290
+ Layer.provide(corsLayer),
291
+ );
292
+ ```
293
+
294
+ Do not read a `.layer` property from `HttpRouter.middleware(...)`.
295
+
296
+ ## Other transports
297
+
298
+ `strictEffectApproach` governs generated HTTP API modules. Effect RPC,
299
+ WebSockets, and other transports remain valid when they are modeled as explicit
300
+ transport surfaces with typed Effect programs. They do not make raw HTTP
301
+ request handlers valid inside generated UltraModern API modules.
@@ -28,6 +28,11 @@ type BuildCacheConfig =
28
28
 
29
29
  Controls the caching behavior during the build process.
30
30
 
31
+ When persistent Rspack cache is enabled, Modern.js treats `cacheDirectory` as a
32
+ base directory and stores each Rsbuild/Rspack environment in its own child
33
+ directory, such as `client`, `server`, or `workerSSR`. This avoids concurrent
34
+ environment compilers writing to the same filesystem cache root.
35
+
31
36
  Modern.js will enable build cache by default to improve the compile speed. You can disable the build cache by setting it to `false`:
32
37
 
33
38
  ```js
@@ -78,12 +78,39 @@ mise exec -- pnpm check
78
78
  mise exec -- pnpm build
79
79
  ```
80
80
 
81
- Strict generated API migration requires `3.5.0-ultramodern.0` or newer.
81
+ Strict generated API migration requires `3.5.0-ultramodern.10` 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, and strict Oxlint boundary
84
- rule set. Agents that cannot install that BleedingDev cohort yet should use the
85
- local Modern.js workspace for migration validation; otherwise pin
86
- `3.5.0-ultramodern.0` or newer with `--ultramodern-package-version`.
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.10` 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.10 ultramodern \
94
+ migrate-strict-effect --version 3.5.0-ultramodern.10
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, framework-owned
101
+ toolchain pins, direct topology API metadata, strict Effect pnpm
102
+ overrides/trust policy, and the pnpm lockfile. Remaining failures are source
103
+ migration work: move code to `shared/api.ts`, `api/index.ts`, and
104
+ `src/api/*-client.ts`, then delete `api/effect`, `api/lambda`, `shared/effect`,
105
+ and `src/effect`.
106
+
107
+ Generated strict Effect workspaces pin the compatible Effect cohort with pnpm
108
+ overrides: `effect@4.0.0-beta.91` and `@effect/vitest@4.0.0-beta.91`. Do not
109
+ add app-local direct Effect versions that disagree with those overrides. They
110
+ also carry generated pnpm policy exclusions for `effect@4.0.0-beta.91` and
111
+ `@effect/opentelemetry@4.0.0-beta.91` so the framework-selected Effect beta
112
+ cohort is not blocked by the 24-hour age gate or trusted-publisher to
113
+ provenance metadata transition.
87
114
 
88
115
  Gradual migration means old, unmigrated Modern.js apps can keep their existing
89
116
  runtime until they are converted. Once a surface is generated or migrated as
@@ -122,6 +149,17 @@ Effect RPC, WebSockets, and other transports should be added as explicit
122
149
  transport surfaces when needed. They do not make raw request handlers valid
123
150
  inside generated HTTP API modules.
124
151
 
152
+ Strict API tests should exercise the `HttpApi` contract. Use
153
+ `createEffectBffTestHandler` from `@modern-js/plugin-bff/effect-edge` for
154
+ edge-compatible proof tests; if you manually compose a web handler, provide
155
+ `HttpServer.layerServices` beside your API group layer before calling
156
+ `HttpRouter.toWebHandler`.
157
+
158
+ For dynamic origin CORS predicates inside strict Effect HTTP APIs, prefer
159
+ `HttpRouter.middleware(Effect.succeed(HttpMiddleware.cors(...)))`.
160
+ `HttpRouter.middleware(...)` returns a `Layer` directly in the pinned Effect
161
+ cohort; do not read a `.layer` property from it.
162
+
125
163
  Use one package-source strategy per repo. The published BleedingDev create
126
164
  package defaults to `--ultramodern-package-source=install` and records the
127
165
  exact cohort in `.modernjs/ultramodern.json`. Keep `--workspace` only for local
@@ -148,6 +186,13 @@ manual edits. Fix topology, ownership, package-source, local overlay, generated
148
186
  contract, Tailwind prefix, or Module Federation conflicts in the owning files
149
187
  instead of patching generated output by hand.
150
188
 
189
+ Cloudflare D1 bindings are first-class on `deploy.worker.d1Databases`; use that
190
+ config instead of app-local postprocessing when a generated app owns D1
191
+ migrations. Cloudflare public output excludes server-only `api` and `shared`
192
+ directories by default, and generated Modern/Rspack caches are isolated per app,
193
+ build target, and Rspack environment so local `build`, `cloudflare:build`, and
194
+ multi-environment compilers do not share the same filesystem cache directory.
195
+
151
196
  ## Human Workflow
152
197
 
153
198
  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,103 @@ 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.10` 使用:
199
+
200
+ ```yaml
201
+ minimumReleaseAgeExclude:
202
+ - 'effect@4.0.0-beta.91'
203
+ - '@effect/opentelemetry@4.0.0-beta.91'
204
+
205
+ trustPolicyExclude:
206
+ - 'effect@4.0.0-beta.91'
207
+ - '@effect/opentelemetry@4.0.0-beta.91'
208
+
209
+ overrides:
210
+ '@effect/vitest': 4.0.0-beta.91
211
+ effect: 4.0.0-beta.91
212
+ ```
213
+
214
+ 不要在应用包里添加不同版本的直接 `effect` 依赖。Effect beta 不一致时,Layer 或 HTTP
215
+ middleware 构建可能因为运行时 service 来自不同包实例而失败。保留生成的 policy
216
+ exclusion 条目并让它跟随同一个 Effect 版本组;它用于处理框架选定 Effect beta 版本在
217
+ 24 小时成熟期内的安装,以及 `effect` 和 `@effect/opentelemetry` 从 trusted-publisher
218
+ metadata 迁移到 provenance attestation 时的 pnpm no-downgrade 检查。
219
+
220
+ ## 契约测试
221
+
222
+ 严格 Effect API 测试应当测试声明的 `HttpApi` 契约,而不是原始 request handler。
223
+ Edge 兼容测试可以使用框架 helper:
224
+
225
+ ```ts
226
+ import { createEffectBffTestHandler } from '@modern-js/plugin-bff/effect-edge';
227
+ import apiModule from '../api/index';
228
+
229
+ const testApi = await createEffectBffTestHandler({
230
+ module: apiModule,
231
+ prefix: '/api',
232
+ });
233
+
234
+ const response = await testApi.handler(new Request('http://localhost/api/ping'));
235
+ ```
236
+
237
+ 如果低层 proof 必须手动组合 web handler,需要在调用 `HttpRouter.toWebHandler` 前提供
238
+ `HttpServer.layerServices`:
239
+
240
+ ```ts
241
+ import {
242
+ HttpApiBuilder,
243
+ HttpRouter,
244
+ HttpServer,
245
+ Layer,
246
+ } from '@modern-js/plugin-bff/effect-server';
247
+
248
+ const handler = HttpRouter.toWebHandler(
249
+ HttpApiBuilder.layer(api).pipe(
250
+ Layer.provide(apiGroupLayer),
251
+ Layer.provide(HttpServer.layerServices),
252
+ ),
253
+ ).handler;
254
+ ```
255
+
256
+ 优先使用生成 helper,除非测试必须检查底层 Effect router 组合。
257
+
258
+ ## 动态 CORS
259
+
260
+ 需要动态 origin predicate 时,使用 Effect HTTP middleware 作为 Layer。在当前锁定的
261
+ Effect v4 beta 版本组中,`HttpRouter.middleware(...)` 直接返回 `Layer`:
262
+
263
+ ```ts
264
+ import {
265
+ Effect,
266
+ HttpApiBuilder,
267
+ HttpMiddleware,
268
+ HttpRouter,
269
+ Layer,
270
+ } from '@modern-js/plugin-bff/effect-server';
271
+
272
+ const corsLayer = HttpRouter.middleware(
273
+ Effect.succeed(
274
+ HttpMiddleware.cors({
275
+ allowedOrigins: origin =>
276
+ origin.endsWith('.example.com') ? origin : undefined,
277
+ }),
278
+ ),
279
+ );
280
+
281
+ const layer = HttpApiBuilder.layer(api).pipe(
282
+ Layer.provide(apiGroupLayer),
283
+ Layer.provide(corsLayer),
284
+ );
285
+ ```
286
+
287
+ 不要从 `HttpRouter.middleware(...)` 读取 `.layer` 属性。
288
+
289
+ ## 其他传输
290
+
291
+ `strictEffectApproach` 约束生成的 HTTP API 模块。Effect RPC、WebSocket 和其他传输在
292
+ 建模为显式传输 surface、并使用类型化 Effect 程序时仍然有效。它们不让生成的
293
+ UltraModern API 模块中的原始 HTTP request handler 变得有效。
@@ -28,6 +28,10 @@ type BuildCacheConfig =
28
28
 
29
29
  控制 Modern.js 在构建过程中的缓存行为。
30
30
 
31
+ 启用 Rspack 持久化缓存时,Modern.js 会把 `cacheDirectory` 视为基础目录,并为每个
32
+ Rsbuild/Rspack environment 写入独立子目录,例如 `client`、`server` 或
33
+ `workerSSR`。这样可以避免并发 environment compiler 写入同一个文件系统 cache 根目录。
34
+
31
35
  在构建时,Modern.js 默认会开启构建缓存来提升二次构建的速度。如果不希望缓存,你可以将 `buildCache` 置为 `false` 将其禁用掉:
32
36
 
33
37
  ```js
@@ -71,11 +71,34 @@ mise exec -- pnpm check
71
71
  mise exec -- pnpm build
72
72
  ```
73
73
 
74
- 严格生成 API 迁移要求 `3.5.0-ultramodern.0` 或更新版本。
74
+ 严格生成 API 迁移要求 `3.5.0-ultramodern.10` 或更新版本。
75
75
  `3.4.0-ultramodern.20` 及更早 cohort 还没有这套完整的直接
76
- `api/index.ts` 生成器、生成的 `.mts` 检查和严格 Oxlint 边界规则。还不能安装该
77
- BleedingDev cohort 的 agent 应使用本地 Modern.js workspace 做迁移校验;否则用
78
- `--ultramodern-package-version` 固定 `3.5.0-ultramodern.0` 或更新版本。
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.10` 或更新版本。
80
+
81
+ 手写 package alias 或生成 metadata 之前,先在目标 workspace 运行框架迁移命令:
82
+
83
+ ```bash
84
+ pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.10 ultramodern \
85
+ migrate-strict-effect --version 3.5.0-ultramodern.10
86
+ pnpm api:check
87
+ pnpm contract:check
88
+ ```
89
+
90
+ 该命令会更新 `.modernjs/ultramodern.json`、根 `modernjs.packageSource`、生成的
91
+ Modern package alias、框架拥有的 toolchain pin、直接 topology API metadata、严格 Effect
92
+ pnpm overrides/trust policy 和 pnpm lockfile。剩余失败就是源码迁移:把代码移到
93
+ `shared/api.ts`、`api/index.ts` 和 `src/api/*-client.ts`,再删除 `api/effect`、
94
+ `api/lambda`、`shared/effect` 和 `src/effect`。
95
+
96
+ 严格 Effect 生成 workspace 会通过 pnpm overrides 固定兼容版本组:
97
+ `effect@4.0.0-beta.91` 和 `@effect/vitest@4.0.0-beta.91`。不要添加与这些
98
+ overrides 冲突的 app 本地直接 Effect 版本。生成的 pnpm policy 还会为
99
+ `effect@4.0.0-beta.91` 和 `@effect/opentelemetry@4.0.0-beta.91` 添加例外,避免框架选定的
100
+ Effect beta 版本组被 24 小时成熟期检查,或从 trusted-publisher metadata 迁移到
101
+ provenance attestation 时被 no-downgrade 检查误拦截。
79
102
 
80
103
  渐进迁移意味着尚未迁移的旧 Modern.js app 可以保留现有 runtime,直到被转换为
81
104
  UltraModern。一个 surface 一旦生成为或迁移为 UltraModern HTTP API,就只能使用
@@ -110,6 +133,15 @@ shim 来让旧布局通过。
110
133
  Effect RPC、WebSockets 和其他传输方式应在需要时作为显式 transport surface 增加。
111
134
  它们不能作为在生成 HTTP API 模块中重新引入原始 request handler 的理由。
112
135
 
136
+ 严格 API 测试应执行 `HttpApi` 契约。Edge 兼容 proof 测试使用
137
+ `@modern-js/plugin-bff/effect-edge` 的 `createEffectBffTestHandler`;如果必须手动组合
138
+ web handler,在调用 `HttpRouter.toWebHandler` 前要把 `HttpServer.layerServices` 和
139
+ API group layer 一起提供。
140
+
141
+ 动态 origin CORS predicate 使用
142
+ `HttpRouter.middleware(Effect.succeed(HttpMiddleware.cors(...)))`。在固定的 Effect
143
+ 版本组中,`HttpRouter.middleware(...)` 直接返回 `Layer`;不要读取 `.layer` 属性。
144
+
113
145
  每个仓库只使用一种 package-source 策略。已发布的 BleedingDev create 包默认使用
114
146
  `--ultramodern-package-source=install`,并把精确 cohort 记录到
115
147
  `.modernjs/ultramodern.json`。`--workspace` 只用于本地 monorepo
@@ -127,6 +159,12 @@ CommonJS,同时生成的 app package 继续为 Module Federation DTS 保留稳
127
159
  ownership、package-source、本地 overlay、生成契约、Tailwind prefix 或 Module
128
160
  Federation 冲突,都应在对应归属文件中修复,不要手写补丁覆盖生成结果。
129
161
 
162
+ Cloudflare D1 绑定使用一等配置 `deploy.worker.d1Databases`;生成 app 拥有 D1
163
+ migration 时不要再用 app 本地 postprocess。Cloudflare public output 默认排除服务端
164
+ `api` 和 `shared` 目录,并且生成的 Modern/Rspack cache 会按 app、构建目标和 Rspack
165
+ environment 隔离,避免本地 `build`、`cloudflare:build` 以及多 environment compiler
166
+ 共享同一个文件系统 cache 目录。
167
+
130
168
  ## 人类工作流
131
169
 
132
170
  公开的 BleedingDev create 包只有一个受支持的生成产品。默认命令会创建一个
package/package.json CHANGED
@@ -19,24 +19,24 @@
19
19
  "modern.js",
20
20
  "ultramodern.js"
21
21
  ],
22
- "version": "3.5.0-ultramodern.0",
22
+ "version": "3.5.0-ultramodern.10",
23
23
  "publishConfig": {
24
24
  "registry": "https://registry.npmjs.org/",
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "mermaid": "^11.15.0",
29
- "@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.5.0-ultramodern.0"
28
+ "mermaid": "^11.16.0",
29
+ "@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.5.0-ultramodern.10"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@rsbuild/plugin-sass": "2.0.0",
33
- "@rspress/core": "2.0.14",
34
- "@rspress/plugin-llms": "2.0.14",
35
- "@rspress/shared": "2.0.14",
33
+ "@rspress/core": "2.0.15",
34
+ "@rspress/plugin-llms": "2.0.15",
35
+ "@rspress/shared": "2.0.15",
36
36
  "@shikijs/transformers": "^4.3.0",
37
37
  "@types/fs-extra": "11.0.4",
38
38
  "@types/node": "^26.0.1",
39
- "@typescript/native-preview": "7.0.0-dev.20260624.1",
39
+ "@typescript/native-preview": "7.0.0-dev.20260628.1",
40
40
  "classnames": "^2.5.1",
41
41
  "clsx": "^2.1.1",
42
42
  "fs-extra": "^11.3.5",