Skip to content

spec 双源清账 C14:HttpMethod(./api, ./shared ≠ ./ui)—— 1 条,且两侧是「7 值 vs 5 值」的真分歧,不是同形别名 #4691

Description

@os-zhuang

#4535 的 HttpMethod 簇。原排在 v18,维护者 2026-08-02 裁定并进 v17。基线行:

HttpMethod — [./api, ./shared (type)] ≠ [./ui (type)]

⚠️ 派发前 PM 已做过定位复核,推翻了「与 C11 同形、一行即可」的初判。 请先读下面这节再动手 —— C11(#4688 / PR #4689)的结论不适用于本簇

这两个 HttpMethod 是真的不同类型

packages/spec/src/shared/http.zod.ts 里并存两个 HTTP 方法枚举(行号对 origin/main @ 7d21581,开工请自行复核):

位置 声明 取值
shared/http.zod.ts:20 + :30 export const HttpMethod / export type HttpMethod 7 值 — GET POST PUT DELETE PATCH HEAD OPTIONS
shared/http.zod.ts:37 export const HttpMethodSchema = lazySchema(…) 5 值 — GET POST PUT PATCH DELETE
shared/http.zod.ts:39 export type HttpMethodType = z.infer<typeof HttpMethodSchema> 5 值(就是 ./ui 那个类型,只是叫别的名)
ui/view.zod.ts:2040 export type HttpMethod = z.infer<typeof HttpMethodSchema> 5 值,却叫 HttpMethod

:37 的注释自己写明了它的身份:"HTTP Method Schema (subset for UI/View data sources)"

所以 ./shared / ./apiHttpMethod 是 7 值,./uiHttpMethod5 值真子集同一个名字,两个不同类型 —— 这正是 #4411 陷阱最恶劣的形态(C11 那簇只是符号身份不同、形状相同;本簇形状也不同)。

⛔ 因此「让 ./ui re-export ./shared 的 HttpMethod」是错的

那会把 ./uiHttpMethod 从 5 值悄悄放宽到 7 值,而 HttpRequestSchema.method(shared/http.zod.ts:48)用的是 5 值的 HttpMethodSchema。结果是类型说 HEAD / OPTIONS 合法、运行时 .parse() 直接抛 —— 类型开始对运行时说谎。这比双源本身更糟,不要为了清一行基线换来这个。

推荐路线(PM 分析,实施者需复核;复核结论若与此矛盾请升级而不是硬做)

./ui 不再导出 HttpMethod 这个名字,冲突即消失(名字只剩 ./api + ./shared 的那一个声明),且类型保持诚实的 5 值。

  • ui/view.zod.ts:2040export type HttpMethod = z.infer<typeof HttpMethodSchema>
  • 需要给 ./ui 保留一个可用的方法类型名的话:export type { HttpMethodType } from '../shared/http.zod'(纯新增,安全)。./ui 本来就 re-export 了 HttpMethodSchema(ui/view.zod.ts:37),消费者也可自行 z.infer
  • 迁移指引:import type { HttpMethod } from '@objectstack/spec/ui'import type { HttpMethodType } from '@objectstack/spec/shared'(同一个类型,零形状变化)。

风险面 —— PM 已实测,仓内为零

  • 全仓没有任何地方从 ./ui 导入 HttpMethod 唯一的跨包消费者 packages/rest/src/route-manager.ts:9 用的是 Shared.HttpMethod(7 值那个),不受影响。
  • HttpMethodType 目前零消费者(只有 :39 的声明本身)。
  • 但它仍是已发布的 API 表面,外部消费者不可见 → api-surface.json 会变,changeset 定 major,写清 FROM → TO。
  • 不是 authorable key,不需要 ADR-0087 tombstone / conversion。 若你的实现产生了任何一个,说明走偏了,停下来升级。

纪律(#4535 §1–§4 + 手册 6/7/8)

  1. ⛔ 禁止手编 packages/spec/authorable-surface.json(authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650)。
  2. ⚠️ 门禁绿 ≠ 登记正确(build-schemas.ts 检查 (b) 用叶名匹配 conversion surface —— 无关簇的 .type 就能让一个 tombstone 冒充「已登记迁移」 #4659):自己枚举核对,别信 leaf-name 匹配。
  3. 回归 pin 必须能真的红。 packages/spec 的「编译期 pin」是失效的:tsconfig 排除了 *.test.ts,vitest 也不做类型检查 #4642 已证本包编译期 pin 空转(tsconfig.json 排除 **/*.test.ts、vitest 不开 typecheck)。HttpMethod类型,运行时看不见 —— 直接抄 PR refactor(spec): 双源 C11 收敛 — HttpRequest 类型别名改为 re-export ./shared 的唯一声明 (#4688) #4689ui/view.test.ts 里那条用 TypeScript compiler API 在 src/ 上做符号身份解析的断言(含它的防空转守卫 expect(moduleSym).toBeTruthy()),那是本仓目前唯一有效的类型级 pin 形态。必须 sabotage 验证并贴实际输出。
  4. 本簇额外要求一条值域 pin:钉住 ./sharedHttpMethod 是 7 值、HttpMethodSchema 是 5 值,且 HttpRequestSchema.parse({url, method:'HEAD'}) 抛错。这条防的是将来有人「顺手统一」两个枚举而不自知。
  5. ⚠️ 生成物冲突只能靠重新生成(手册第 7 条)。推之前重新 git fetch origin main;spec-changes.json 是对象数组,必须跑 gen:spec-changes,集合合并会丢条目。
  6. 不要碰 content/docs/releases/

验收

  • 基线删掉 HttpMethod 那 1 行,只减不增(其余行一字不动)。
  • ./ui 不再导出 HttpMethod;./api / ./shared 的 7 值声明原样不动
  • 全绿:buildcheck:dual-source-exportscheck:generatedtest,加源码审计组(check:liveness / check:strictness-ledger / check:empty-state / check:variant-docs / check:exported-any / check:skill-examples),以及全仓 pnpm typecheck
  • changeset 一份,@objectstack/spec major,含 FROM → TO 与迁移指引。
  • 三/四条 pin 全部 sabotage 验证并贴输出。

顺带记录,本簇不处理

HttpMethodType(5 值)零消费者、HttpMethod(7 值)与它并存且命名无法自解释("Type" 后缀是噪音,而且它是更窄的那个)。按 ADR-0049 enforce-or-remove 与 ADR-0112 D9(a)「给领域专用的那一侧改名」,更彻底的收敛是把 5 值那对改成 ViewHttpMethod / ViewHttpMethodSchema 之类自解释的名。但那超出清一行基线的范围,范围由维护者定 —— 想做请单独立单,不要搭本簇顺风车。

关联:#4535(主单)、#4688 / PR #4689(C11,同文件相邻行,但结论不适用本簇)、#4642(pin 空转)、#4650 / #4659(门禁洞)、#4675(生成物冲突)、ADR-0049、ADR-0087、ADR-0112

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions