Skip to content

fix: enveloper 逃生舱 markSent + leizm/web send 404 + zod peerDep + 测试文档#193

Merged
yourtion merged 4 commits into
mainfrom
fix/test-and-envelope-escape
Jul 1, 2026
Merged

fix: enveloper 逃生舱 markSent + leizm/web send 404 + zod peerDep + 测试文档#193
yourtion merged 4 commits into
mainfrom
fix/test-and-envelope-escape

Conversation

@yourtion

@yourtion yourtion commented Jul 1, 2026

Copy link
Copy Markdown
Owner

背景

在 one-api 项目接入 erest(@leizm/web adapter + enveloper 模式)时发现三个问题,本 PR 一并修复。三框架(Express / Koa / @leizm/web)行为对齐,向后兼容。

问题与修复

1. @erest/leizmweb reply.send 映射错误导致纯文本响应 404

根因createLeiReply.send 调用 ctx.response.send(body),但 @leizm/web 的 response 没有 send 方法(仅有 end/json/file)。由于用了可选链 leiRes.send?.(),调用静默跳过 → 响应体未写 → 框架 final handler 返回 404。

影响面@leizm/web adapter 下任何用 ctx.reply.send(文本) 的路由此前都会 404。此前未暴露是因为现有测试/用例多走 reply.json(json 方法存在、工作正常)。

修复send 映射到 response.end(body) + 补 text/plain; charset=utf-8 content-type。

验证:新增 src/test/test-multilevel-route.ts,三框架对齐测试 forceGroup 下 :param 多层路径 + register/registerTyped 混合(复现用例:leizm/web 下 .register()/items/:id/export 返回 404,Express/Koa 正常)。

2. enveloper 与手动写非 JSON 响应冲突(核心)

根因registerTyped 的 wrappedHandler 在 handler return 后无条件ctx.__returned = trueapi.ts),wrapWithEnvelope__returned 即调 successEnveloper → reply.json()不检查响应是否已手动发送。当 handler 需返回非 JSON(CSV/文件下载/流式)并经 raw 手动写完响应后,enveloper 会再包一层 json,可能覆盖已发送响应或触发「headers already sent」(三框架行为不一,靠框架兜底是脆弱巧合)。

修复(显式标记法,向后兼容)

  • Reply 接口新增公开方法 markSent():handler 手动写完响应后调用,告知 enveloper 跳过自动包装。
  • wrapWithEnvelope 收紧决策:__returned && !reply.__sent 才包装。
  • 三 adapter(express/koa/leizmweb)reply 实现统一维护 __sent 标记:json/send/markSent 均置位,三框架行为对齐。
  • 不调 markSent 的存量 handler 行为不变(纯新增)。

验证test-envelope.ts 加单元测试(reply.__sent=true → enveloper 跳过)+ 三框架集成测试(registerTyped + enveloper + raw 写 CSV + markSent → 响应体保持原始 CSV,非信封)。

3. 测试引擎 .input() 的方法归属未文档化

agent.ts.input() 对 GET/DELETE 归 query、POST/PUT/PATCH 归 body(HTTP DELETE body 有争议,fetch/浏览器/代理普遍不传递)。这是合理的测试设计,但未文档化,用户注册 DELETE 路由用 body schema 时会踩坑(测试侧 .input() 归 query、schema 从 body 读 → MISSING_PARAM)。

修复:纯文档化。README 测试章节 + agent.ts JSDoc 显式说明,补 DELETE 走 query schema 的示例与警示。

4. zod 从 dependencies 改为 peerDependencies(连带修复,Breaking)

接入时发现:erest 此前把 zod@^4.0.5 列为 hard dependency,任何 link/本地路径依赖 erest 的项目会出现两份 zod 副本(erest 自带 + 宿主自带),TS 因 zod 内部版本字面量标记(_zod.version.minor)不同而报类型不兼容(如 4.4.3 vs 4.0.5)。

修复:zod 从 dependencies 移到 peerDependencies(^4.0.0,required),由宿主统一提供版本——schema 校验库的标准做法。devDependencies 保留 zod@^4.4.3 供本仓库开发。

Breaking:宿主项目须在自身 dependencies 显式声明 zod。adapter 子包不直接依赖 zod,无需改动。详见 MIGRATION.md v3.2.1。

验证

  • pnpm run check(lint + format):0 errors ✅
  • pnpm run build + build:packages:全绿 ✅
  • pnpm run test:cov317 测试全过,覆盖率达标(lines 92.33% / branches 82.69% > 80% 门槛 / functions 96.96% > 95% 门槛)✅
  • pnpm --filter erest-example test:13 测试全过(端到端 dist 产物)✅
  • 三框架对齐人工核对:test-raw.tstest-register-typed.tstest-group.tstest-integration-{koa,leizmweb}.ts + 新增用例 ✅

文档

  • README:测试 .input() 方法归属说明 + DELETE 走 query 示例;raw 逃生舱章节新增 markSent() 小节。
  • agent.ts:.input() JSDoc 补方法归属约定。
  • MIGRATION.md:新增 v3.2.1 章节,登记 markSent(新增)、leizmweb send 修复、DELETE input 文档化、zod peerDep(breaking)。

提交结构

  • fix(leizmweb): reply.send 映射到 response.end(修复纯文本响应 404)
  • feat(envelope): Reply.markSent() 逃生舱,手动响应不被 enveloper 二次包装
  • docs: 文档化 DELETE input 归属 + markSent 逃生舱 + leizmweb send 修复说明
  • refactor(deps): zod 改为 peerDependencies(消除 link 场景的 zod 副本冲突)

版本:当前 3.2.0 → 本 PR 内容计划作为 3.2.1 发布(MIGRATION 已登记)。

yourtion added 4 commits July 1, 2026 00:31
根因:LeizmWebAdapter.createLeiReply.send 调用 ctx.response.send(body),
但 @leizm/web 的 response 没有 send() 方法(仅有 end/json/file)。由于用了
可选链 leiRes.send?.(body),调用静默跳过 → 响应体未写 → 框架 final handler
返回 404。

影响面:在 @leizm/web adapter 下,任何用 ctx.reply.send(文本) 的路由
(register 或 registerTyped)都会 404。此前未暴露是因为现有测试与用例多走
reply.json(json 方法存在、工作正常)。

修复:send 映射到 response.end(body),并补 text/plain; charset=utf-8
content-type,与 json() 的 application/json 对齐。

复现测试(src/test/test-multilevel-route.ts):三框架对齐验证 forceGroup 下
「:param 多层路径 + 同前缀多路由」形态,覆盖 data/export/data:pk/refresh
四类路径(register 与 registerTyped 混合),确保三框架行为一致。
问题:registerTyped handler 若需返回非 JSON 响应(CSV/文件下载/流式)并经 raw
手动写完响应,handler return 后 wrappedHandler 无条件置 __returned=true,wrapWithEnvelope
见 __returned 即调 successEnveloper → reply.json(),可能覆盖已发送的响应或触发
「headers already sent」类错误(三框架行为不一,靠框架兜底是脆弱巧合)。

修复:显式标记法(向后兼容,不破坏现有 __returned 机制)
- Reply 接口新增公开方法 markSent():handler 手动写完响应后调用,告知 enveloper 跳过。
- wrapWithEnvelope 收紧决策:__returned && !reply.__sent 才包装。
- 三 adapter(express/koa/leizmweb)reply 实现统一维护 __sent 标记:
  json/send/markSent 均置 __sent=true,三框架行为对齐。

测试(test-envelope.ts,三框架对齐):
- 单元:reply.__sent=true → enveloper 跳过(不调 successEnveloper/json)。
- 三框架集成:registerTyped + enveloper + raw 写 CSV + markSent → 响应体保持原始 CSV,
  非 {success:true,data} 信封,content-type 为 text/csv。

非 breaking:markSent 为纯新增方法,不调它的现有 handler 行为不变。
- README 测试章节:显式说明 .input() 对 GET/DELETE 归 query、POST/PUT/PATCH 归 body,
  补 DELETE 走 query schema 的示例与警示。
- README raw 逃生舱章节:新增 markSent() 小节,说明 enveloper 模式下手动写非 JSON
  响应(CSV/下载/流)后须调 markSent 跳过自动包装,含三框架示例。
- agent.ts .input() JSDoc:补充方法归属约定说明。
- MIGRATION.md:新增 v3.2.1 章节,登记 markSent(新增)、leizmweb send 修复、
  DELETE input 文档化三项。
问题:erest 此前把 zod@^4.0.5 列为 hard dependency。任何通过 link/本地路径依赖
erest 的项目会出现两份 zod 副本(erest 自带 + 宿主自带),TS 因 zod 内部版本
字面量标记(_zod.version.minor)不同而报类型不兼容(如 4.4.3 vs 4.0.5)。

修复:zod 从 dependencies 移到 peerDependencies(^4.0.0,required),
由宿主项目统一提供版本——这是 schema 校验库的标准做法。devDependencies
保留 zod@^4.4.3 供本仓库开发/测试用。

Breaking:宿主项目须显式声明 zod 依赖。MIGRATION.md v3.2.1 登记迁移说明。
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.47%. Comparing base (a7b19ee) to head (bb9835a).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #193   +/-   ##
=======================================
  Coverage   92.47%   92.47%           
=======================================
  Files          28       28           
  Lines        2882     2883    +1     
  Branches      748      747    -1     
=======================================
+ Hits         2665     2666    +1     
  Misses        215      215           
  Partials        2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yourtion
yourtion merged commit 5993ba9 into main Jul 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant