Skip to content

spec/src/cloud/ 的 request 类型只导出 z.infer(OUTPUT),没有 Input 孪生 —— cloud 仓调用方被迫手写 zod 自己负责填的默认值(#4963 house convention 的又一个分区) #5975

Description

@hotlong

做 cloud#1118 批次 1(objectstack-ai/cloud#1184,把 cloud 仓测试文件接进 tsc)时量到。不在那个 PR 范围内(跨仓契约面),按 Prime Directive #10 单独记在这里,unassigned。

#5543 / #5227根因,与 #5507 / #5551形状(按 zod 文件分区登记 #4963 确立的 X / XParsed house convention)。本条是 spec/src/cloud/ 这个分区,此前没有单子覆盖。

事实

packages/spec/src/cloud/environment.zod.ts:310cloud/tenant.zod.ts 等只导出 OUTPUT 一侧:

export type ProvisionEnvironmentRequest = z.infer< typeof ProvisionEnvironmentRequestSchema >;
export type TenantRoutingConfig         = z.infer< typeof TenantRoutingConfigSchema >;
export type ProvisionTenantRequest      = z.infer< typeof ProvisionTenantRequestSchema >;

而这些 schema 里有 .default() 字段,例如 environment.zod.ts:304

visibility: EnvironmentVisibilitySchema.optional().default('private')

于是 visibility 在 OUTPUT 类型上是必填。名字叫 ...Request 的类型,描述的却是解析之后的形状。

后果(在 cloud 仓实测)

packages/service-tenant/src/environment-provisioning.ts:340 把它用作自己那次 parse 的入参类型

async provisionEnvironment(
  request: ProvisionEnvironmentRequest,   // OUTPUT 类型
): Promise< ProvisionEnvironmentResponse > {
  const parsed = ProvisionEnvironmentRequestSchema.parse(request);
  ...

调用方写一个完全合法的 provisioning 请求:

svc.provisionEnvironment({
  organizationId: 'org-123',
  displayName: 'Alice dev',
  createdBy: 'user-1',
});

得到 TS2345: Property 'visibility' is missing。即:类型要求调用方手写 zod 负责填的默认值。cloud#1118 批次 1 里,service-tenant 13 条报错中的 12 条是这一形状(visibility ×8、jwtOrganizationClaim/tenantHeaderName ×2、plan ×2),tenant-router 的 3 条同族。批次 1 只能在测试侧按默认值原样补齐绕过去,生产者侧没动。

一个佐证:源码自己不相信这个类型

同文件 environment-provisioning.ts:621

visibility: parsed.visibility ?? 'private',

parsed.parse() 的产物,visibility.default('private'),所以它永远不可能是 undefined —— 这行 ?? 'private' 是死代码。作者在防一个类型说不可能、而直觉说可能的情况;直觉是对的(对入参而言可能),类型是错的(它描述的是出参)。

为什么记成 finding 而不是 bug

今天没有用户会撞到运行时错误:默认值该填的都填了,行为正确。代价是作者态的 —— 合法的调用写不出来、?? 死代码被当成防御留着、以及 cloud 仓测试为了过类型而多写 12 处默认值。严重度请按 triage 定。

建议(不代裁决)

#4963 的 house convention,给 spec/src/cloud/ 这批 request 类型补 Input 孪生(z.input),并把 provisionEnvironment 这类「自己 parse 自己入参」的方法形参改指 Input 侧。这是参数类型放宽,对现有调用方不破坏。是否连带清掉 ?? 'private' 这类死防御,可另议。

关联:#4963(convention)、#5543(objectql registerObject 同根因)、#5227ApiEndpoint.authRequired 同根因)、#5507 / #5551(其他分区)、#5478(spec 自己那 691 条台账里的同族)、objectstack-ai/cloud#1118 / objectstack-ai/cloud#1184(本条的测量来源)。

Blocked-by: #5551


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions