Skip to content

feat: v3.2 类型安全 state + 全局响应信封 + 便利 schema#192

Merged
yourtion merged 14 commits into
mainfrom
feat/typed-state-envelope
Jun 30, 2026
Merged

feat: v3.2 类型安全 state + 全局响应信封 + 便利 schema#192
yourtion merged 14 commits into
mainfrom
feat/typed-state-envelope

Conversation

@yourtion

Copy link
Copy Markdown
Owner

Summary

由 one-api phase2-server dogfooding 驱动的三块 ergonomics 改进:

  • typed stateERest<T, Raw, State> 实例级泛型,ctx.state 类型安全(消除鉴权值 as 断言)。State 须用 type alias 定义。
  • 全局 response envelopesetResponseEnvelopers({success, error, testUnwrapper}) 注册后,registerTyped handler 进入 return 模式(return data → 框架自动包装信封),告别手写 ok()/fail() + 每处 reply.json(ok(x))
  • 便利 schema + 测试返回类型z.anyObject() 别名(等价 z.object({}).catchall(z.unknown()));success<T>() 返回类型从 response schema 推导。

附带:registerTyped handler 第二参数从 reply 统一为 ctxContext<State, Raw>,含 ctx.reply + ctx.state),保留 raw 逃生舱强类型。

Breaking(仅当启用新特性时)

  • registerTyped handler 签名 (req, reply) => void(req, ctx) => data
  • ctx.state 类型由 State 泛型驱动(默认 Record<string, unknown> 向后兼容)

详见 MIGRATION.md

Test Plan

  • erest:287 测试全绿(含新增 test-any-object / test-state-types / test-envelope)
  • erest:typecheck exit 0 + oxlint 0 errors + oxfmt 格式正确
  • one-api phase2-server 适配验证:31 e2e 全绿,typecheck 无错(净减 29 行)
  • 向后兼容:未注册 enveloper / 未声明 State / 未声明 response schema 时维持 v3.1 行为

yourtion added 12 commits June 29, 2026 23:10
register(fn: T) 改为 register(fn: Middleware):当 T=Middleware(createERest 锁定)时,
handler 的 (ctx, next) 参数由 TS 自动推导,无需手写类型标注。

此前 register(fn: T) 中 T 是类级泛型,TS 无法从函数字面量反推参数类型,导致
ctx/next 始终 implicit any,使用者被迫手写标注或定义中间桥接类型。

全量 276 测试通过。
由 one-api phase2-server dogfooding 驱动的三块框架改进:
- 痛点1:ctx.state 实例级泛型 StateMap,消除鉴权值 as 断言
- 痛点4:全局 response envelope + registerTyped 强制 return,删手写 ok()/fail()
- 痛点3+6:z.anyObject() 便利别名 + 测试返回类型从 response schema 推导

向后兼容为硬约束:State 默认值/未注册 enveloper 维持 v3.1 行为。
按依赖顺序:
- Task 1: z.anyObject() 便利别名
- Task 2: 测试 success<T>() 返回类型从 response schema 推导
- Task 3: typed state(ERest<State> 泛型)+ registerTyped handler 第二参数统一为 ctx
- Task 4: 全局 response envelope + wrapWithEnvelope dispatcher 接入
- Task 5: setResponseEnvelopers 接受 testUnwrapper 便捷拆信封
- Task 6: one-api phase2-server 同步适配验证

关键时序:Task 3 统一 handler 签名(reply→ctx),后续 Task 测试均用新签名。
向后兼容为硬约束。
erest 导出的 z 新增 anyObject() 语义化别名,供「动态字段 body」场景使用。
用 Proxy 透传原生 z + 注入 anyObject,不污染 Zod 原生命名空间。
同时具名导出 zAnyObject 常量。
Task 1 subagent 擅自把 .sort() 改成 .toSorted(),引入 tsc 错误。
回归到原始 .sort()(原地排序,语义等价)。
- TestAgent.success<T>() 泛型化(默认 unknown 向后兼容)
- buildTest<T> 透传泛型,getter 返回泛型函数表达式
- 调用侧 .get<UserVO>(path).success<UserVO>() 时 T 由 response schema 推导
- test-test.ts 新增运行时 + 类型层断言用例
- Context<State, Raw> / Middleware<State> 加泛型(默认 Record<string,unknown> 向后兼容)
- ctx.state 类型安全;ctx.reply.raw 强类型(Raw 经 ERest<T,Raw,State> 锁定)
- registerTyped handler 签名 (req, reply) → (req, ctx),ctx 含 ctx.reply
- 三子包 createERest<State>() 透传 State 泛型
- 存量测试同步适配(reply → ctx.reply)
- 新增 test-state-types(类型推导)+ state 跨中间件传递测试
- 顺手:isMissing 提到 compileValidate 外层(消除 oxlint scoping error)
- setResponseEnvelopers({success, error, testUnwrapper}):注册后 handler 进入 return 模式
- wrapWithEnvelope 工具:包装 dispatch,成功用 successEnveloper 包 return 值,错误用 errorEnveloper 包
- registerTyped wrappedHandler:统一 await handler 返回值,写入 __returned + __returnValue
- FrameworkAdapter.bindRoute + 三子包接入 envelopers 参数
- testUnwrapper 便捷入口(等价 setFormatOutput,供测试拆信封)
- 未注册 enveloper 时退化为原 dispatch(v3.1 向后兼容,零开销)
- 新增 test-envelope(成功/错误/undefined 三场景)
breaking: registerTyped handler 第二参数 reply→ctx;ctx.state 类型由 State 泛型驱动
新增: z.anyObject() / success<T>() / setResponseEnvelopers
- examples/src/api.js: registerTyped handler (req, reply) → (req, ctx),reply.xxx → ctx.reply.xxx
- README.md: 代码示例与说明同步(ctx.reply / ctx.reply.raw 逃生舱)
- 完整 test 全绿(lib 287 + example 13)
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.01124% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.47%. Comparing base (1c1cc2e) to head (d4bff4b).

Files with missing lines Patch % Lines
src/lib/params.ts 58.33% 5 Missing ⚠️
src/lib/index.ts 90.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #192      +/-   ##
==========================================
+ Coverage   91.64%   92.47%   +0.82%     
==========================================
  Files          28       28              
  Lines        2837     2882      +45     
  Branches      722      748      +26     
==========================================
+ Hits         2600     2665      +65     
+ Misses        235      215      -20     
  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 added 2 commits June 30, 2026 21:54
- erest-gen codegen 模板:(req, reply) → (req, ctx)(影响生成代码的用户)
- erest-gen README 示例同步
- test-raw-types / test-builder-types:reply.raw → ctx.reply.raw
- api.ts registerTyped JSDoc:(req, reply) → (req, ctx)
- README 修正 sed 误伤的 ctx.ctx.reply
在现有测试文件补充(不新建):
- test-envelope.ts: wrapWithEnvelope 6 分支单测(零开销退化/success包装/不二次包装/
  error包装/re-throw/undefined)+ enveloper 模式下 response schema 校验(通过+失败)
- test-router.ts: compose 洋葱模型 6 单测(顺序/终止/reject/同步抛错/重复next/空链)

lib/adapters/utils.ts 覆盖率 27.77% → 100%,整体 92.33% 达标。
@yourtion
yourtion merged commit a7b19ee into main Jun 30, 2026
7 of 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