@bleedingdev/modern-js-main-doc 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21
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/components/deploy-command.mdx +1 -0
- package/docs/en/components/hono.mdx +3 -3
- package/docs/en/components/init-app.mdx +1 -5
- package/docs/en/components/prerequisites.mdx +1 -1
- package/docs/en/configure/app/bff/effect.mdx +34 -36
- package/docs/en/configure/app/source/react-compiler.mdx +2 -0
- package/docs/en/guides/advanced-features/bff/data-platform.mdx +2 -2
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +33 -21
- package/docs/en/guides/advanced-features/bff/function.mdx +2 -2
- package/docs/en/guides/advanced-features/bff/operators.mdx +17 -17
- package/docs/en/guides/basic-features/render/ssr-cache.mdx +14 -1
- package/docs/en/guides/get-started/ultramodern.mdx +14 -97
- package/docs/en/plugin/server-plugins/api.mdx +1 -0
- package/docs/zh/components/bff-operator-code.mdx +1 -1
- package/docs/zh/components/deploy-command.mdx +1 -0
- package/docs/zh/components/hono.mdx +3 -3
- package/docs/zh/components/prerequisites.mdx +1 -1
- package/docs/zh/configure/app/bff/effect.mdx +28 -33
- package/docs/zh/configure/app/source/react-compiler.mdx +2 -0
- package/docs/zh/guides/advanced-features/bff/data-platform.mdx +2 -2
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +31 -20
- package/docs/zh/guides/advanced-features/bff/function.mdx +2 -2
- package/docs/zh/guides/advanced-features/bff/operators.mdx +17 -17
- package/docs/zh/guides/basic-features/render/ssr-cache.mdx +14 -1
- package/docs/zh/guides/get-started/ultramodern.mdx +17 -83
- package/docs/zh/plugin/server-plugins/api.mdx +1 -0
- package/package.json +10 -10
- package/src/sandbox/csr-auth/src/routes/Auth-tsx.txt +10 -6
- package/src/sandbox/csr-auth/src/routes/page-tsx.txt +1 -0
- package/ultramodern-preset/package.json +2 -2
|
@@ -39,7 +39,7 @@ export interface CacheControl {
|
|
|
39
39
|
}
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
其中 `customKey` 为自定义缓存 key
|
|
42
|
+
其中 `customKey` 为自定义缓存 key。默认情况下,缓存 key 由请求的 origin、归一化后的 pathname(去除末尾斜杠)以及 query 组成。当设置了 `customKey` 后,它会完全替代默认 key:`customKey` 回调函数接收的是归一化后的 pathname(而非 `CacheOptionProvider` 的 `req`),并需要自行负责整个分区(例如按租户、host 或 query 区分)——不能将原始 cookie 或其他凭证嵌入返回的 key 中。
|
|
43
43
|
|
|
44
44
|
**Function 类型**
|
|
45
45
|
|
|
@@ -111,6 +111,19 @@ export const cacheOption: CacheOption = {
|
|
|
111
111
|
|
|
112
112
|
上述 `/home` 和 `/about` 将会作为模式进行匹配,这意味着 `/home/abc` 也会匹配上该规则。同时,你也可以在其中编写正则语法:`/home/.+`
|
|
113
113
|
|
|
114
|
+
### 缓存策略
|
|
115
|
+
|
|
116
|
+
以下策略由框架自动执行,返回的 `CacheControl` 无法覆盖:
|
|
117
|
+
|
|
118
|
+
- 只有 `GET` 请求会被缓存。
|
|
119
|
+
- 请求携带 `Cookie` 或 `Authorization` 请求头时会跳过缓存,除非设置了 `customKey`(自定义 key 被视为一份明确的分区约定)。
|
|
120
|
+
- 请求的 `Cache-Control` 为 `private`、`no-cache` 或 `no-store` 时会跳过缓存。
|
|
121
|
+
- 只有当响应为 `200`、`Cache-Control` 中不含 `private`/`no-cache`/`no-store`、不含 `Set-Cookie`、也不含非空的 `Vary` 头时才会被写入缓存;否则会删除该 key 已有的缓存条目,而不是写入新的。
|
|
122
|
+
- 当 `stale` 状态触发的重新渲染结果不再可缓存时(例如变为按用户区分),本次已返回的 stale 响应不受影响,但缓存条目会被清除,后续请求将不再命中该缓存。
|
|
123
|
+
- `CacheOptionProvider` 返回 `false` 依然会完全禁用该请求的缓存,不受上述策略影响。
|
|
124
|
+
|
|
125
|
+
缓存 key 带有命名空间(`__ssr__cache:v2:...`)。旧版本未加命名空间的缓存条目(包括自定义 `Container` 中已存储的条目)不会再被读取,只会依据容器自身的淘汰/TTL 机制自然过期;不存在全局缓存清空操作。
|
|
126
|
+
|
|
114
127
|
### 缓存容器
|
|
115
128
|
|
|
116
129
|
默认情况下,Server 将会使用内存进行缓存。但通常情况下服务将会部署在 Serverless 容器上。每一次的服务访问可能都是一个新的进程,这样每次访问都不能应用缓存。
|
|
@@ -16,15 +16,11 @@ UltraModern.js 3.0 是我们从 Modern.js 分叉出来的 SuperApp 框架。它
|
|
|
16
16
|
- 仅在跨团队稳定性场景增加平台契约。
|
|
17
17
|
- 保留显式 escape hatch,但它们必须位于生成 HTTP API 模块之外。
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## 当前 Workspace 契约
|
|
20
20
|
|
|
21
|
-
UltraModern.js
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- 既有项目结构与命令使用方式。
|
|
25
|
-
- 渐进式接入路径(应用无需大规模重构)。
|
|
26
|
-
|
|
27
|
-
UltraModern.js 的增强能力是新 SuperApp 的默认产品面。框架方向是 Effect + TanStack + SSR + Micro Verticals,生成的 HTTP API 只走严格 Effect HttpApi surface。既有 Modern.js 应用可以渐进迁移;一旦某个 API surface 被生成为或迁移为 UltraModern HTTP API,就必须使用严格 Effect HttpApi 路径。
|
|
21
|
+
UltraModern.js 使用 Effect HttpApi、TanStack Router、SSR 和可独立部署的 Micro Verticals。
|
|
22
|
+
生成的 workspace 使用 `api/index.ts`、`shared/api.ts`、`src/api/*` 和具体的 Effect schema。
|
|
23
|
+
CLI 按当前契约创建、扩展和验证 workspace。
|
|
28
24
|
|
|
29
25
|
## 有意引入的差异(3.0 线)
|
|
30
26
|
|
|
@@ -50,20 +46,9 @@ UltraModern.js 的增强能力是新 SuperApp 的默认产品面。框架方向
|
|
|
50
46
|
- 生成的 UltraModern HTTP API 只走严格 Effect HttpApi surface;原始 request handler 和 Hono/file-function API 不属于生成架构。
|
|
51
47
|
- 除非稳定性硬需求,否则避免引入破坏性 API 变更。
|
|
52
48
|
|
|
53
|
-
##
|
|
54
|
-
|
|
55
|
-
对于已经在使用 Modern.js 3.0 或旧版 BleedingDev UltraModern scaffold 的团队,迁移路径是把 API 工作迁到严格 Effect HttpApi surface,而不是保留旧的原始 handler 布局。
|
|
56
|
-
|
|
57
|
-
1. 既有 Modern.js 应用在尚未进入生成 UltraModern surface 前可以继续按现状运行。TanStack Router 是新脚手架与增量迁移的优先路径,团队可以按自己的节奏迁移路由层。
|
|
58
|
-
2. 新建或迁移中的 BFF 能力使用 `bff.runtimeFramework: 'effect'`、`bff.effect.entry: './api/index'` 和 `bff.effect.strictEffectApproach: true`。接口先改 `shared/api.ts` 的 `HttpApi` 契约,再在 `api/index.ts` 用 `defineEffectBff(...)` / `HttpApiBuilder` 实现。
|
|
59
|
-
3. 在生成或已迁移的 UltraModern workspace 中,把 raw handler、`api/lambda/**`、手写 `Response` 和手写 request parsing 视为迁移缺陷。
|
|
60
|
-
4. 这套公开预设现在已经附带显式的发布 / 认证 gate。生成 workspace 会自带 `.github/workflows/ultramodern-workspace-gates.yml`,因此 `pnpm check` 与 `pnpm build` 从第一天开始就是本地接入契约的一部分;CI 会以并行矩阵运行这些基础 gate。
|
|
61
|
-
|
|
62
|
-
旧版生成 workspace 迁移时,以已发布的包 cohort 作为事实来源:
|
|
49
|
+
## 创建并验证 Workspace
|
|
63
50
|
|
|
64
51
|
```bash
|
|
65
|
-
pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest --help
|
|
66
|
-
pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest catalog --vertical --dry-run
|
|
67
52
|
pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest catalog --vertical
|
|
68
53
|
mise install
|
|
69
54
|
mise exec -- pnpm install
|
|
@@ -71,82 +56,31 @@ mise exec -- pnpm check
|
|
|
71
56
|
mise exec -- pnpm build
|
|
72
57
|
```
|
|
73
58
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
server runtime 中。请求 `/` 时,框架会按语言协商在服务端返回 `302`,例如
|
|
78
|
-
`Accept-Language: cs-CZ` 会跳转到 `/cs`,英语或 fallback 流量会跳转到 `/en`。
|
|
79
|
-
|
|
80
|
-
既有生成 workspace 应整体升级同一个 BleedingDev Modern package cohort。
|
|
81
|
-
先解析当前 cohort 版本,再运行同版本迁移命令:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
COHORT="$(npm view @bleedingdev/modern-js-ultramodern-create version)"
|
|
85
|
-
pnpm dlx "@bleedingdev/modern-js-ultramodern-create@$COHORT" ultramodern \
|
|
86
|
-
migrate-strict-effect --version "$COHORT"
|
|
87
|
-
pnpm install
|
|
88
|
-
pnpm check
|
|
89
|
-
pnpm build
|
|
90
|
-
pnpm cloudflare:build
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Cloudflare Workers 已部署环境需要在 cohort 更新后重新部署,并在接受迁移前验证 root
|
|
94
|
-
响应:
|
|
59
|
+
本地化 Cloudflare SSR workspace 在服务端将 `/` 重定向到协商的语言路径。
|
|
60
|
+
`Accept-Language: cs-CZ` 返回 `302`、`Location: /cs`、
|
|
61
|
+
`Cache-Control: private, no-store`,以及覆盖语言检测 Header 的 `Vary`。
|
|
95
62
|
|
|
96
63
|
```bash
|
|
97
64
|
curl -I -H 'Accept-Language: cs-CZ,cs;q=0.9,en;q=0.1' https://<worker-host>/
|
|
98
65
|
```
|
|
99
66
|
|
|
100
|
-
期望响应是 `302`,并带有 `Location: /cs` 或匹配的 locale、
|
|
101
|
-
`Cache-Control: private, no-store`,以及覆盖语言检测 Header 的 `Vary`。继续跟随该
|
|
102
|
-
redirect 后,应返回匹配语言的 SSR 页面,文档语言和 i18n SSR 数据也应一致。
|
|
103
|
-
|
|
104
|
-
不要通过 app 自有 root route、客户端 redirect、自定义导航 wrapper、Cloudflare Worker
|
|
105
|
-
postprocess、生成产物编辑或本地 redirect shim 来修复旧版本的 root `404`。如果升级后
|
|
106
|
-
`/` 仍然返回 `404`,先确认生产构建日志显示的
|
|
107
|
-
`Modern.js Framework v<cohort-version>` 就是迁移目标 cohort,然后重新部署 Worker。
|
|
108
|
-
|
|
109
|
-
严格生成 API 迁移是当前每个 cohort 的组成部分:直接 `api/index.ts` 生成器、生成的
|
|
110
|
-
`.mts` 检查、严格 Oxlint 边界规则、Effect 版本组 overrides 和严格 Effect 迁移命令。
|
|
111
|
-
还不能安装 BleedingDev cohort 的 agent 应使用本地 Modern.js workspace 做迁移校验;
|
|
112
|
-
否则用 `--ultramodern-package-version` 固定目标 cohort。
|
|
113
|
-
|
|
114
|
-
手写 package alias 或生成 metadata 之前,先在目标 workspace 运行框架迁移命令:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
COHORT="$(npm view @bleedingdev/modern-js-ultramodern-create version)"
|
|
118
|
-
pnpm dlx "@bleedingdev/modern-js-ultramodern-create@$COHORT" ultramodern \
|
|
119
|
-
migrate-strict-effect --version "$COHORT"
|
|
120
|
-
pnpm api:check
|
|
121
|
-
pnpm contract:check
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
该命令会更新 `.modernjs/ultramodern.json`、根 `modernjs.packageSource`、生成的
|
|
125
|
-
Modern package alias、框架拥有的 toolchain pin、直接 topology API metadata、严格 Effect
|
|
126
|
-
pnpm overrides/trust policy 和 pnpm lockfile。剩余失败就是源码迁移:把代码移到
|
|
127
|
-
`shared/api.ts`、`api/index.ts` 和 `src/api/*-client.ts`,再删除 `api/effect`、
|
|
128
|
-
`api/lambda`、`shared/effect` 和 `src/effect`。
|
|
129
|
-
|
|
130
67
|
严格 Effect 生成 workspace 会通过 pnpm overrides 固定兼容版本组:
|
|
131
|
-
`effect@4.0.0-rc.
|
|
132
|
-
`@effect/vitest@4.0.0-rc.
|
|
68
|
+
`effect@4.0.0-rc.117`、`@effect/opentelemetry@4.0.0-rc.117` 和
|
|
69
|
+
`@effect/vitest@4.0.0-rc.117`。不要添加与这些 overrides 冲突的 app 本地直接
|
|
133
70
|
Effect 版本。严格的 24 小时发布年龄门禁适用于实际安装的包;当前版本组不包含 Effect
|
|
134
71
|
年龄豁免,仅用于 override 的 `@effect/vitest` 也不是已安装的审批目标。
|
|
135
|
-
`trustPolicyExclude` 是独立策略,仅为 `effect` 和
|
|
136
|
-
`@effect/opentelemetry` 保留精确例外,以处理 trusted-publisher metadata 向
|
|
137
|
-
provenance attestation 的迁移;它不属于 release-age 审批。
|
|
138
72
|
|
|
139
73
|
### 当前生成依赖基线
|
|
140
74
|
|
|
141
75
|
| 范围 | 固定版本 |
|
|
142
76
|
| --- | --- |
|
|
143
|
-
| Effect runtime 与测试版本组 | `effect`、`@effect/opentelemetry`、`@effect/vitest`:`4.0.0-rc.
|
|
144
|
-
| Effect compiler | `@effect/tsgo@0.
|
|
145
|
-
| Oxc 与 Ultracite | `oxlint@1.
|
|
146
|
-
| TanStack Router | `@tanstack/react-router@1.170.
|
|
147
|
-
| Module Federation | `bridge-react`、`manifest`、`modern-js-v3`、`rspack`:`2.9.
|
|
77
|
+
| Effect runtime 与测试版本组 | `effect`、`@effect/opentelemetry`、`@effect/vitest`:`4.0.0-rc.117` |
|
|
78
|
+
| Effect compiler | `@effect/tsgo@0.45.0` |
|
|
79
|
+
| Oxc 与 Ultracite | `oxlint@1.85.0`、`oxfmt@0.70.0`、`ultracite@7.12.0` |
|
|
80
|
+
| TanStack Router | `@tanstack/react-router@1.170.39`、`@tanstack/router-core@1.171.32`、`@tanstack/history@1.162.4` |
|
|
81
|
+
| Module Federation | `bridge-react`、`manifest`、`modern-js-v3`、`rspack`:`2.9.2`;`@module-federation/node@2.7.52` |
|
|
148
82
|
| Tailwind CSS | `tailwindcss@4.3.3` |
|
|
149
|
-
| Node 与包管理工具 | Node `26.7.0`、`@types/node@^26.
|
|
83
|
+
| Node 与包管理工具 | Node `26.7.0`、`@types/node@^26.6.2`、pnpm `11.27.1` |
|
|
150
84
|
|
|
151
85
|
生成的 workspace 默认使用 `@module-federation/bridge-react` 的
|
|
152
86
|
router-free base entry(`bridge.enableBridgeRouter: false`),应用路由由
|
|
@@ -190,7 +124,7 @@ Effect RPC、WebSockets 和其他传输方式应在需要时作为显式 transpo
|
|
|
190
124
|
它们不能作为在生成 HTTP API 模块中重新引入原始 request handler 的理由。
|
|
191
125
|
|
|
192
126
|
严格 API 测试应执行 `HttpApi` 契约。Edge 兼容 proof 测试使用
|
|
193
|
-
`@modern-js/
|
|
127
|
+
`@modern-js/bff-effect/effect-edge` 的 `createEffectBffTestHandler`;如果必须手动组合
|
|
194
128
|
web handler,在调用 `HttpRouter.toWebHandler` 前要把 `HttpServer.layerServices` 和
|
|
195
129
|
API group layer 一起提供。
|
|
196
130
|
|
|
@@ -191,6 +191,7 @@ api.onPrepare(async () => {
|
|
|
191
191
|
- `'file-change'`: 文件变化事件
|
|
192
192
|
- `event.payload`: 当 `type` 为 `'file-change'` 时,包含文件变化信息数组。
|
|
193
193
|
- **执行阶段:** 当文件发生变化或需要重新打包时。
|
|
194
|
+
- **重新打包顺序:** 对于 `'repack'`,开发服务器会按注册顺序等待处理函数完成,再从 require 缓存中清除上一代服务端产物。某个处理函数抛出错误时,其后的处理函数不再执行。新请求会等待处理函数完成;已在执行的请求继续使用已加载的模块。重新打包处理函数不能等待发往开发服务器的请求,因为该请求会等待处理函数完成。
|
|
194
195
|
- **示例:**
|
|
195
196
|
|
|
196
197
|
```typescript
|
package/package.json
CHANGED
|
@@ -19,29 +19,29 @@
|
|
|
19
19
|
"modern.js",
|
|
20
20
|
"ultramodern.js"
|
|
21
21
|
],
|
|
22
|
-
"version": "3.9.0-ultramodern.
|
|
22
|
+
"version": "3.9.0-ultramodern.21",
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.9.0-ultramodern.
|
|
28
|
-
"@modern-js/ultramodern-sandpack-profile": "npm:@bleedingdev/modern-js-ultramodern-sandpack-profile@3.9.0-ultramodern.
|
|
27
|
+
"@modern-js/sandpack-react": "npm:@bleedingdev/modern-js-sandpack-react@3.9.0-ultramodern.21",
|
|
28
|
+
"@modern-js/ultramodern-sandpack-profile": "npm:@bleedingdev/modern-js-ultramodern-sandpack-profile@3.9.0-ultramodern.21",
|
|
29
29
|
"mermaid": "^11.17.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@rsbuild/plugin-sass": "2.0.1",
|
|
33
|
-
"@rspress/core": "2.0.
|
|
34
|
-
"@rspress/plugin-llms": "2.0.
|
|
35
|
-
"@rspress/shared": "2.0.
|
|
33
|
+
"@rspress/core": "2.0.22",
|
|
34
|
+
"@rspress/plugin-llms": "2.0.22",
|
|
35
|
+
"@rspress/shared": "2.0.22",
|
|
36
36
|
"@shikijs/transformers": "^4.4.3",
|
|
37
37
|
"@types/fs-extra": "11.0.4",
|
|
38
|
-
"@types/node": "^26.
|
|
38
|
+
"@types/node": "^26.6.2",
|
|
39
39
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
40
40
|
"classnames": "^2.5.1",
|
|
41
41
|
"clsx": "^2.1.1",
|
|
42
|
-
"fs-extra": "^11.4.
|
|
43
|
-
"react": "^19.
|
|
44
|
-
"react-dom": "^19.
|
|
42
|
+
"fs-extra": "^11.4.1",
|
|
43
|
+
"react": "^19.3.0",
|
|
44
|
+
"react-dom": "^19.3.0",
|
|
45
45
|
"rsbuild-plugin-open-graph": "1.1.3",
|
|
46
46
|
"ts-node": "^10.9.2",
|
|
47
47
|
"typescript": "^7.0.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Navigate,
|
|
3
|
-
|
|
3
|
+
useMatch,
|
|
4
4
|
useNavigate,
|
|
5
5
|
} from '@modern-js/plugin-tanstack/runtime';
|
|
6
6
|
import React from 'react';
|
|
@@ -52,8 +52,9 @@ export function AuthStatus() {
|
|
|
52
52
|
Welcome {auth.user}!{' '}
|
|
53
53
|
<button
|
|
54
54
|
type="button"
|
|
55
|
-
onClick={() => {
|
|
56
|
-
|
|
55
|
+
onClick={async () => {
|
|
56
|
+
await navigate({ to: '/' });
|
|
57
|
+
auth.signout(() => {});
|
|
57
58
|
}}
|
|
58
59
|
>
|
|
59
60
|
Sign out
|
|
@@ -62,9 +63,12 @@ export function AuthStatus() {
|
|
|
62
63
|
);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
export function RequireAuth({ children }: { children: JSX.Element }) {
|
|
66
|
+
export function RequireAuth({ children }: { children: React.JSX.Element }) {
|
|
66
67
|
const auth = useAuth();
|
|
67
|
-
const
|
|
68
|
+
const pathname = useMatch({
|
|
69
|
+
strict: false,
|
|
70
|
+
select: match => match.pathname,
|
|
71
|
+
});
|
|
68
72
|
|
|
69
73
|
if (!auth.user) {
|
|
70
74
|
// Redirect them to the /login page, but save the current location they were
|
|
@@ -74,7 +78,7 @@ export function RequireAuth({ children }: { children: JSX.Element }) {
|
|
|
74
78
|
return (
|
|
75
79
|
<Navigate
|
|
76
80
|
replace
|
|
77
|
-
search={{ redirect:
|
|
81
|
+
search={{ redirect: pathname }}
|
|
78
82
|
to="/login"
|
|
79
83
|
/>
|
|
80
84
|
);
|