feat: reply.raw 原生能力逃生舱 + 全自动 Raw 泛型(v3.1.0) - #191
Merged
Conversation
bindRoute 的 .catch 由吞错写死 {error} 改为 ctx.next(err) re-throw,与 express adapter
(next(err))/ koa adapter(throw err)+ README「app 级错误中间件」约定一致。此前 leizmweb 是
唯一特例:吞错使 app 级错误中间件失效、丢失 ERestError.code、无法自定义信封。
补 test-integration-leizmweb 错误 re-throw 集成测试(实跑验证 app 级中间件收到错并写信封)。
全量 280 测试通过(core 267 + examples 13)。
引入框架泛型驱动的 reply.raw 逃生舱,让 registerTyped handler 能拿到强类型 原生对象(setCookie/redirect/stream 等);审计三框架能力差异并提供可选统一 错误格式器。错误处理 re-throw 语义不动(commit 727b2c0 已对齐)。
8 个 TDD 任务:Reply 泛型(1) → API 透传(2) → ERest/IGroup 透传(3) → 错误格式器(4) → Express/Koa/leizmweb adapter(5/6/7) → 类型测试+文档+deprecated(8)
- Reply<Raw> 接口新增 readonly raw 字段,status 返回类型改为 this - FrameworkAdapter<T, Raw> 加 Raw 维度(方法签名不变) - Context.reply 标注 Reply<unknown>(before/middleware 不暴露 raw) tsc 全量通过:三框架子包 createXxxReply 因结构兼容未报错, Task 5/6/7 将在 createReply 时真正塞入 raw 值。
- 类级加 Raw 泛型维度(默认 unknown) - registerTyped handler 的 reply 参数类型用 Reply<Raw> - wrappedHandler 调用处 ctx.reply 断言为 Reply<Raw>(运行时即 adapter 注入的 reply, Context 静态类型为 Reply<unknown>,断言模式同 typedReq) - 现有 5 个方法级 Zod 泛型不变,Raw 与之正交 - tsc 通过,267 测试全通过
- genSchema/IGroup/IApiInfo 加 Raw 维度(默认 unknown) - ERest<T, Raw>,registAPI/defineAPI 字段类型返回 API<T, Raw> - group() 返回 IGroup<T, Raw>,new API<T, Raw> - API.define<T, Raw> 静态方法加 Raw 维度,defineAPI 调用处显式传 Raw - 现有 new ERest() 行为不变(Raw 默认 unknown),tsc 通过,267 测试全通过
工具函数,用户在 app 级错误中间件调用,实现三框架统一错误响应体
({error, code} 结构,statusCode 取 ERestError.statusCode 或 status,默认 400)。
不改 adapter、不破坏 re-throw 对齐(commit 727b2c0)。
经 index.ts 的 export * 自动从核心包导出。
- createExpressReply 接收 req/res,reply.raw = { req, res }(热路径零分配,
仅多挂闭包已有引用)
- 导出 ExpressRaw 类型别名;ExpressAdapter implements FrameworkAdapter<T, ExpressRaw>
- 新增 createERest() 工厂构造时锁定 Raw 泛型(handler reply.raw 零标注强类型)
- test-raw.ts 验证 Express handler 通过 reply.raw.res.cookie 设置 Set-Cookie
- http-req helper 扩展 headers 字段(验证响应头所需,纯增量不破坏现有测试)
- createKoaReply 返回 Reply<KoaRaw>,reply.raw = 原生 ctx(热路径零分配) - 导出 KoaRaw 类型别名;KoaAdapter implements FrameworkAdapter<T, KoaRaw> - 新增 createERest() 工厂构造时锁定 Raw 泛型 - test-raw.ts 验证 Koa handler 通过 reply.raw.cookies.set 设置 Set-Cookie (Koa cookies 库输出小写 httponly,断言大小写不敏感)
- createLeiReply 返回 Reply<LeizmWebRaw>,reply.raw = 原生 ctx(热路径零分配) - 导出 LeizmWebRaw 类型别名(@leizm/web Context) - LeizmWebAdapter implements FrameworkAdapter<T, LeizmWebRaw> - 新增 createERest() 工厂构造时锁定 Raw 泛型 - test-raw.ts 验证 handler 通过 reply.raw.response.setHeader 设置响应头 (leizmweb cookie API 随版本变化,用 setHeader 作稳定证明)
- test-builder-types.ts:新增 reply.raw 默认 unknown 类型断言 - test-raw-types.ts:验证 Express createERest 锁定 Raw,reply.raw 零标注强类型 - README:新增「原生能力逃生舱 reply.raw」章节 + 三框架能力速查表 - ERest 构造函数标记 @deprecated,引导到子包 createERest() - 修复 leizmweb ctx 断言(Record→LeizmWebRaw 需经 unknown,子包独立 build 严格) - npm test 全绿:核心 276 + examples 13
- test-error-formatter: AUTH_REQUIRED → PERMISSION_DENIED(前者不在 ERestErrorCode 联合类型, PERMISSION_DENIED 语义匹配 401) - leizmweb: raw 双重断言补注释说明为何需经 unknown(结构不充分重叠) - MIGRATION.md: 补充 reply.raw 逃生舱 + createERest 工厂说明(非破坏性,标记 deprecated) 最终 code review 遗留的 Critical(issue1) 与文档类 issue4/5 已处理。 issue2(测试文件未纳入类型检查)为既有基础设施缺口,非本次引入,留作后续。
本次 feature(reply.raw 逃生舱 + 全自动 Raw 泛型 + createERest 工厂 + defaultErrorFormatter)为非破坏性 minor,bump 版本: - erest 核心 3.0.1 → 3.1.0 - @erest/express / @erest/koa / @erest/leizmweb 3.0.1 → 3.1.0 - @erest/gen 0.1.0 → 3.1.0(统一 scope 版本,gen 本次未改动) - MIGRATION.md 新增 3.1.0 changelog 节(替换之前 append 的临时段) 内部依赖均用 workspace: 协议,无需手改。npm test 全绿(276+13)。
修复 CI 失败的 2 个 oxlint error(本 PR 引入):
- test-integration-leizmweb.ts: ctx 参数未使用 → _ctx
- adapters/types.ts: FrameworkAdapter 的 Raw 泛型未使用 → JSDoc 说明 + 局部
eslint-disable(Raw 为类型标记,供子类 implements 锁定,不在方法签名出现)
新增 pre-commit hook 防止 CI 再次失败:
- husky 9 + lint-staged 17
- .husky/pre-commit 跑 lint-staged
- lint-staged 对暂存的 *.{ts,js} 跑 oxfmt --write + oxlint --fix
- 已验证:oxlint error(如未使用参数)会阻止 commit
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #191 +/- ##
==========================================
+ Coverage 91.59% 91.64% +0.04%
==========================================
Files 28 28
Lines 2821 2837 +16
Branches 719 722 +3
==========================================
+ Hits 2584 2600 +16
Misses 235 235
Partials 2 2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
v3.0 的标准化抽象(
Reply)只有status/json/send三个方法,导致 setCookie、redirect、流式响应等底层能力在registerTypedhandler 里完全无法访问。逐个补 Reply 方法不可行——三框架签名永远对不齐(Expressres.cookie()/ Koactx.cookies.set()/ leizmweb 各不同)。本 PR 引入框架泛型驱动的
reply.raw逃生舱,让 handler 能拿到强类型的原生对象;同时审计三框架能力差异,提供可选的统一错误格式器。非破坏性(现有new ERest()代码零改动)。设计文档:
docs/superpowers/specs/2026-06-29-raw-escape-hatch-and-capability-alignment.md主要变更
1.
reply.raw逃生舱Reply<Raw>接口新增readonly raw: Raw字段,handler 通过reply.raw访问原生对象Context.reply保持Reply<unknown>—— before/middleware/hook 不暴露 raw(仅 handler 拿,避免 onError 与 re-throw 双重写入)2. 全自动 Raw 泛型 +
createERest()工厂ERest<T, Raw>/API<T, Raw>/IGroup<T, Raw>/genSchema<T, Raw>全链路透传 RawcreateERest(),构造时锁定 Raw,handler 内reply.raw零标注强类型:new ERest()标记@deprecated(过渡期保留,Raw 默认unknown)3. 可选
defaultErrorFormatter工具函数(不改 adapter、不破坏 re-throw 对齐),用户在 app 级错误中间件调用统一三框架错误响应体。
4. 三框架能力速查表
cookie/stream/redirect 差异统一通过
raw+ README 速查表暴露,不再逐个补 Reply 方法。附带
727b2c0:leizmweb 错误处理对齐 express/koa(re-throw 给 app 级错误中间件)测试
new ERest()向后兼容)tsc --noEmit全量通过不在范围