Skip to content

Commit 1c1cc2e

Browse files
authored
Merge pull request #191 from yourtion/feat/adapter-setcookie-error-align
feat: reply.raw 原生能力逃生舱 + 全自动 Raw 泛型(v3.1.0)
2 parents 7728444 + 72b869e commit 1c1cc2e

24 files changed

Lines changed: 2124 additions & 56 deletions

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pnpm exec lint-staged

MIGRATION.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,61 @@ npx erest-gen handler --from ./schemas/user.ts --group user --out ./handlers/use
167167

168168
---
169169

170+
## 3.1.0 — reply.raw 逃生舱 + 框架能力对齐
171+
172+
3.1.0 是 **3.0 的 minor 版本**,引入框架原生能力逃生舱与统一错误格式器,**非破坏性变更**
173+
(现有 `new ERest()` 代码零改动,examples 测试全绿)。
174+
175+
### 1. `reply.raw` 逃生舱
176+
177+
`Reply` 接口新增 `readonly raw: Raw` 字段(`status()` 返回类型从 `Reply` 收紧为 `this`)。
178+
registerTyped 的 handler 通过 `reply.raw` 访问框架原生对象,解决 setCookie / redirect /
179+
stream / 文件下载等底层能力此前完全无法访问的问题。
180+
181+
`Context.reply` 仍保持 `Reply<unknown>`——before/middleware/hook 不暴露 raw,仅 handler 拿到
182+
(避免 onError hook 与 re-throw 语义冲突的双重写入)。
183+
184+
### 2. 全自动 Raw 泛型 + `createERest()` 工厂
185+
186+
`ERest<T, Raw>` / `API<T, Raw>` / `IGroup<T, Raw>` / `genSchema<T, Raw>` 全链路透传 Raw,
187+
registerTyped handler 的 reply 类型随之用 `Reply<Raw>`。子包工厂在**构造时**锁定 Raw:
188+
189+
```diff
190+
- const api = new ERest({ info, groups, forceGroup: true });
191+
+ import { createERest } from "@erest/express"; // 或 @erest/koa / @erest/leizmweb
192+
+ const api = createERest({ info, groups, forceGroup: true }); // reply.raw 零标注强类型
193+
```
194+
195+
- `createERest()` 由三子包分别导出(`@erest/express|koa|leizmweb`),各自返回
196+
`ERest<Middleware, ExpressRaw|KoaRaw|LeizmWebRaw>`,handler 内 `reply.raw` 自动推导。
197+
-`new ERest()` 已标记 `@deprecated`(过渡期保留,Raw 默认 `unknown``reply.raw` 需断言)。
198+
- 三框架 adapter 在 `createXxxReply` 时把闭包已有的原生对象挂到 `raw`(热路径零额外分配,
199+
Koa/leizmweb 仅多挂引用,Express 多一个 `{req,res}` 字面量)。
200+
201+
### 3. 可选统一错误格式器 `defaultErrorFormatter`
202+
203+
新增工具函数(不改 adapter、不破坏 commit 727b2c0 的 re-throw 对齐),用户在 app 级错误中间件
204+
调用以统一三框架错误响应体:
205+
206+
```typescript
207+
import { defaultErrorFormatter } from "erest";
208+
// Express
209+
app.use((err, _req, res, _next) => {
210+
const { status, body } = defaultErrorFormatter(err); // { status, body: { error, code } }
211+
res.status(status).json(body);
212+
});
213+
```
214+
215+
### 4. 能力对齐:仅 `raw` + 文档速查表
216+
217+
cookie/stream/redirect/headersSent 等原生能力差异不再逐个补 Reply 方法,统一通过 `reply.raw`
218+
暴露,并在 README 提供「三框架原生能力速查表」标注各框架语义差异。错误处理(re-throw 语义)
219+
已在 3.0.x 对齐,本次不动。
220+
221+
> raw 的头部/cookie 操作应在 `reply.json()`/`reply.send()` 之前调用(HTTP 头先于体发送)。
222+
223+
---
224+
170225
## 3.0.1 — 文档与发布修复
171226

172227
3.0.1 是 **3.0 的补丁版本**,无运行时 breaking change,修复发布缺陷与文档错误:

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,43 @@ api.api
434434

435435
无论哪种 API,**响应都通过 `reply` 写入**——不要用框架原生的 `res.json()` / `ctx.body =` / `ctx.response.json()`
436436

437+
### 原生能力逃生舱:`reply.raw`
438+
439+
`reply` 只暴露 `json()/status()/send()` 三个框架无关方法。当需要框架特有能力(setCookie、redirect、流式响应、文件下载等)时,通过 `reply.raw` 访问框架原生对象——它是「逃生舱」,绕开标准抽象直达底层。
440+
441+
`reply.raw` 的类型由 **`ERest<T, Raw>` 的 Raw 泛型**驱动。用子包提供的 `createERest()` 工厂创建实例,会在构造时自动锁定 Raw,handler 内 `reply.raw` 自动强类型、**零标注**
442+
443+
```typescript
444+
import { createERest } from '@erest/express';
445+
// 用 createERest 代替 new ERest:构造时锁定 Raw = ExpressRaw({ req, res })
446+
const api = createERest({ info, groups, forceGroup: true });
447+
448+
api.api.post('/login').group('auth').title('登录').registerTyped(
449+
{ body: LoginSchema },
450+
(req, reply) => {
451+
const user = authenticate(req.body);
452+
// reply.raw 自动推导为 { req: Request; res: Response },无需断言
453+
reply.raw.res.cookie('token', sign(user), { httpOnly: true });
454+
reply.json({ ok: true });
455+
},
456+
);
457+
```
458+
459+
> 直接 `new ERest()` 仍可用(过渡期保留),但 Raw 默认为 `unknown``reply.raw` 需手动断言。新代码推荐用子包 `createERest()`
460+
461+
**框架原生能力速查表**`reply.raw` 在三框架下的形态不同,签名/语义各自遵循原生约定):
462+
463+
| 能力 | Express (`reply.raw`) | Koa (`reply.raw`) | @leizm/web (`reply.raw`) |
464+
|------|------|------|------|
465+
| 设置 cookie | `.res.cookie(name, val, opts)` | `.cookies.set(name, val, opts)` | `.response.setHeader('Set-Cookie', ...)` |
466+
| 读取 cookie | `.req.cookies`(需 cookie-parser) | `.cookies.get(name)` | `.request.cookies` |
467+
| 重定向 | `.res.redirect(code, url)` | `.redirect(url)` | `.response.redirect(...)` |
468+
| 流式响应 | `.res.write()` / `.res.end()` | `.body = stream` | `.response.send()` |
469+
| 设置响应头 | `.res.setHeader(k, v)` | `.set(k, v)` | `.response.setHeader(k, v)` |
470+
| 响应头已发送 | `.res.headersSent` | `.res.headersSent` | 查原生 |
471+
472+
> ⚠️ `raw` 的头部/cookie 操作应在 `reply.json()`/`reply.send()` **之前**调用(HTTP 头先于体发送)。`reply.raw` 是逃生舱,不参与框架无关复用——用了 raw 的 handler 即与具体框架耦合。
473+
437474
## 参数读取:`$params` 与分层访问器
438475

439476
adapter 的 checker 把校验后的参数注入到 erest 的标准化 `ctx` 上,handler 通过它们读取。

0 commit comments

Comments
 (0)