feat(runtime): 端点映射键 —— inputMapping / outputMapping 链内应用(#5040 E5c) - #5167
Merged
Conversation
…#5137) `inputMapping` / `outputMapping` were declared by `ApiEndpointSchema` and read by nothing: an author could write them, publish would accept them, and the endpoint ran as if they were absent — the "parsed, then nothing happens" middle state #5040 exists to end, and the ADR-0049 `declared != enforced` shape. New pure module `packages/runtime/src/api-mapping.ts` is their single reader. Semantics come from the frozen vocabulary's describe text and nothing else, taken in its minimal faithful reading: - `inputMapping` ("Map Request Body to Internal Params") projects the request BODY by dot path, applied after the policy pass and before delegation, so a mapping can never buy a caller past `authRequired` / `rateLimit` and `endpoint-executor.ts` stays a pure delegator. Query params are deliberately NOT merged in — the vocabulary names the body, and merging would invent an unstated precedence rule. - `outputMapping` ("Map Internal Result to Response Body") projects the SUCCESS payload only, preserving the envelope; an error answer is never remapped, so a declaration cannot disguise a failure as data. - A mapping is a projection, not a merge: undeclared fields do not ride along, which makes the outbound side an allow-list. - Absent source => unset target; absent (or empty) key => byte-for-byte passthrough, by reference. - A declaration this runtime cannot serve is refused with a structured 501 NOT_IMPLEMENTED naming the entry — `transform` (no transformation-function registry exists), an unusable path (empty, empty segment, prototype key), or colliding targets. `outputMapping` is judged BEFORE delegation so a broken projection cannot let a `create` insert its record and then fail to answer. `api-mapping.ts` joins the error-envelope conformance scan. Zero live behavior change: a non-empty `apis:` is still rejected at publish until the E7 flip. Part of #5040 (E5c). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 4, 2026 07:21
os-zhuang
enabled auto-merge
August 4, 2026 07:21
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.
Fixes #5137
Part of #5040(E5c),Blocked-by #5129(E5b,已在 main:
81e2744)问题
inputMapping/outputMapping被ApiEndpointSchema声明,被 runtime 读取零次:作者写了、publish 放行、端点跑起来映射什么也不做。这正是 #5040 开篇要消灭的「解析通过然后什么也不发生」中间态,也是 ADR-0049declared ≠ enforced的教科书形状 —— 对 AI 写的元数据尤其糟,静默忽略的键不产生任何信号,错误会一直留在应用里。语义的唯一依据:冻结词表的 describe 文本
词表冻结(#5040),所以这两个键只意味着
packages/spec/src/api/endpoint.zod.ts说的那几句:.describe()inputMappingoutputMappingApiMapping.source/.targetApiMapping.transform本 PR 取其最小忠实解读;文本沉默处一律取表达力最低的一种,并在模块文档里逐条写明为什么 —— 反面是在 runtime 里造一套没有契约声明、publish 门也检查不到的方言。每条选择都是日后可放宽、不可收紧的方向。
target组成,未声明字段不随行 —— 这既是「map A to B」的字面意思,也是词表自带示例的行为(firstName→first_name、user.profile.email→contact.email,见endpoint.test.ts)。出站方向因此天然是一份 allow-list,而apis是平台的对外面(ADR-0121 D3):默认把管线返回的每个内部字段都漏出去,不是可接受的缺省。source读的是请求体,不是 query。 词表写的是 Map Request Body to Internal Params。E5 遗漏面:端点 inputMapping / outputMapping 未实现(设计 §3.4 的 api-mapping.ts),E7 翻转前必须补 #5137 正文与设计 §3.4 曾提到{...query, ...body},本 PR 未采纳:合并两者会凭空发明一条「谁覆盖谁」的优先级规则,而一条没人写下来的优先级规则,作者只能靠请求行为与声明读起来不一致才发现。query 参数照旧原样抵达管线。若维护者认为映射也应看见 query,那是对本解读的兼容放宽(加一个来源),反过来则是破坏性收紧 —— 这也是先取窄的原因。source/target是点路径,仅此而已。a.b.c按.切分,只读自有属性(路径永远够不着原型链成员),数组元素按数字键寻址(records.0.id)。无通配、无过滤、无$语法、无转义 —— 「键里真的带点」不可寻址,这是词表的限制,不是在这里设计一套的许可。source解析不到 ⇒target不写。 映射是投影不是校验器:缺省的可选字段得到「目标缺席」,而不是显式null键或被拒绝的请求。在这里发明必填规则,等于把目标管线已用对象元数据做的校验抄一份更弱的。应用点(设计 §3 的顺序)
落在
api-endpoint-step.ts,endpoint-executor.ts保持纯委派、对映射无感知:inputMapping:策略链通过之后、委派之前 —— 投影执行器看到的那个请求,所以映射永远买不通authRequired/rateLimit;outputMapping:只作用于成功答案的载荷({success, data, meta}的data),包络逐字保留。401 / 429 / 400 / 501 一律不重映射 —— 能把失败重塑成数据的投影,就能把失败藏起来。与Cache-Control只上成功答案是同一条不对称,理由更强。无法服务的声明:响亮拒绝,不静默跳过
结构化 501
NOT_IMPLEMENTED,点名具体条目(如inputMapping[1].transform),带处方:transform—— 全仓不存在「transformation function name」注册表,发明一个是沙箱裁决而非映射细节(stack.zod.ts同名先例:framework#2611 令其 build 期失败)。17.x 立项:建设声明式 ApiEndpoint 执行器(挂载 + matchEndpoint + authRequired/cacheTtl/inputMapping/outputMapping 逐键接线) #5040 §3.4 维持 publish 拒绝,本模块是「绕过 publish 直接metadata.register()」的运行期兜底;a..b)、JS 原型键(__proto__/prototype/constructor,两侧都不可走,声明日益由 AI 书写,不能当作可信到能写原型的来源);target—— 两条写同一路径,或一条写进另一条内部,后者会静默丢弃前者。状态码说明(与派发词的一处偏离,请复核):派发词建议「400 类」;本 PR 用 501 +
NOT_IMPLEMENTED,与endpoint-executor.ts的unsupported分支(script/proxy/ 缺objectParams)完全同类同形 —— 这是同一个范畴的答案(冻结词表中 17.x 不执行的子集),且调用方没有做错任何事,用 4xx 会把缺陷归错人。改回 4xx 只需动一个常量,由 PM 裁定。outputMapping的这道判定在委派之前做:投影坏掉的create不该先插入记录、再拒绝作答。验证
既有测试一行未改(
api-endpoint-step.test.ts只有新增 208 行,0 删除)—— 无声明映射时答案不变,这是本单的验收前提。api-mapping.ts已加入error-envelope.conformance.test.ts的源码扫描名单(与 E4/E5 同处不同行,合并友好)。现网影响
零。非空
apis:在 publish / validate 仍被硬拒(E7 #5111 前不撤),整条端点链结构性不可达。交给 E7(#5111)的两条
transform、不可用路径、互撞target)必须进 publish 门带处方拒绝 —— 本模块是兜底,不该是作者遇到的第一道关口;inputMapping声明在不读 body 的操作(find/get/delete)上时,今天是一条什么也不做的合法声明 —— 是否在 publish 期拒绝,属词表/门的裁决,runtime 侧不擅自发明。🤖 Generated with Claude Code
https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd
Generated by Claude Code