Skip to content

client SDK 的 DeleteDataResult 注释写着 "Spec: DeleteDataResponseSchema" 却声明 deleted: boolean,而该 schema 声明的是 success —— 类型骗过编译器,运行时读到 undefined #5638

Description

@baozhoutao

实现 #5581(兜底 delete 成功体向 spec 收敛)时做 deleted 键读取方全仓测绘撞见,不在该单范围内(#5581 的裁定面明确排除 packages/client,且这一条是 client 公开导出接口的形状问题,blast radius 与 semver 判断都不同),故另立。

事实

packages/client/src/index.ts:235:

/** Spec: DeleteDataResponseSchema */
export interface DeleteDataResult {
  object: string;
  id: string;
  deleted: boolean;
}

注释指名 DeleteDataResponseSchema 为其规范来源,但该 schema(packages/spec/src/api/protocol.zod.ts:472)声明的第三个键是 success,不是 deleted:

export const DeleteDataResponseSchema = lazySchema(() => z.object({
  object: z.string().describe('Object name'),
  id: z.string().describe('Deleted record ID'),
  success: z.boolean().describe('Whether deletion succeeded'),
}));

DeleteDataResult 是 client.data.delete(object, id)(index.ts:4300)与 project 作用域下同名方法(index.ts:4851)的返回类型。两者都是 unwrapResponse / _unwrap 直通,没有任何运行时改写 —— 服务端回什么就是什么,类型只是一句声明。

影响(#5581 之前就已成立)

服务端 DELETE /api/v1/data/:object/:id 在装了 MetadataPlugin 的部署(即常规部署)上回的是 { object, id, success: true }。所以:

  • TS 使用方写 const r = await client.data.delete('task', id); if (r.deleted) … —— 编译通过,运行时 r.deleted === undefined,分支恒假;
  • 反过来写 r.success 则被编译器判为不存在的属性。

即类型声明与它自称的规范、与服务端实际返回的形状都相反,且相反的方向是「编译器为错的那一种背书」。这不是 #5581 引入的:#5581 之前 protocol 路径就已经回 success;#5581 只是把兜底路径也对齐,使这条类型声明从「在精简装配上碰巧对上」变成两条路径都对不上。

建议修点

DeleteDataResult.deleted → success,与 DeleteDataResponseSchema 一致。属 @objectstack/client 公开导出接口的破坏性重命名,需要 changeset 与升级须知;是否同时保留一个 deprecated 的 deleted?: boolean 过渡期,请分诊裁定 —— 注意 contract-first 的口径下「消费端同时认两种键」正是被禁止的形状,#5581 的生产方侧就是照这个口径收敛的。

已确认不涉及的面

packages/client 另有五处 if (res.status === 204) return { deleted: true } 归一化(index.ts:3416 / 3467 / 3536 / 3590 / 3799)。逐条核过路由,分别是 shares revoke、sharing rules delete、reports delete、report schedules unschedule、ai conversations delete —— 没有一条走 /data/:object/:id,与本条及 #5581 的收敛面无交集,不应被一并改动。


Related: #5581 (packages/runtime 侧同一规范的收敛)、#5138(同族的 not-found 侧)

Blocked-by: #5581


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions