schemas/ — Zod Schemas
目录: src/schemas/
schemas/ 是 Claude Code 所有 Zod 校验定义的集合。
为什么集中 Schemas?
分散 schema 的问题
// tools/bash.ts
const args = z.object({ command: z.string() })
// services/mcp.ts
const args = z.object({ command: z.string() }) // 重复
// utils/exec.ts
const args = z.object({ command: z.string() }) // 又重复
集中定义
// schemas/common.ts
export const ShellCommandSchema = z.object({
command: z.string(),
cwd: z.string().optional(),
shell: z.enum(['bash', 'zsh', 'pwsh', 'cmd']).optional(),
timeout: z.number().positive().optional()
})
// 使用
import { ShellCommandSchema } from '~/schemas/common'
Schema 分类
1. API Schemas
// schemas/api.ts
export const MessageSchema = z.object({
role: z.enum(['user', 'assistant', 'system']),
content: z.union([z.string(), z.array(ContentBlockSchema)])
})
export const UsageSchema = z.object({
input_tokens: z.number(),
output_tokens: z.number(),
cache_creation_input_tokens: z.number().optional(),
cache_read_input_tokens: z.number().optional(),
})
2. Tool Schemas
// schemas/tools.ts
export const EditToolSchema = z.object({
file_path: z.string(),
old_string: z.string(),
new_string: z.string(),
replace_all: z.boolean().default(false)
})
export const BashToolSchema = z.object({
command: z.string(),
description: z.string().optional(),
run_in_background: z.boolean().optional(),
timeout: z.number().positive().max(600_000).optional(),
})
3. Config Schemas
// schemas/config.ts
export const ConfigSchema = z.object({
theme: z.enum(['light', 'dark', 'auto']).default('auto'),
model: z.string(),
mode: z.enum(['normal', 'plan', 'bypass', 'safe']).default('normal'),
keybindings: z.record(z.string()).optional(),
hooks: HooksSchema.optional(),
mcp: z.object({
servers: z.record(MCPServerSchema)
}).optional(),
})
4. Plugin Schemas
// schemas/plugins.ts
export const PluginManifestSchema = z.object({
name: z.string().min(1).max(64).regex(/^[a-z0-9-]+$/),
version: z.string().regex(/^\d+\.\d+\.\d+/),
description: z.string().max(200),
permissions: z.array(PermissionSchema),
// ...
})
5. Permission Schemas
// schemas/permissions.ts
export const PermissionRuleSchema = z.object({
tool: z.string(),
pattern: z.string().optional(),
pathPattern: z.string().optional(),
decision: z.enum(['allow_once', 'allow_session', 'allow_always', 'deny_once', 'deny_always'])
})
Schema 组合
Zod 支持组合:
const UserPublicSchema = z.object({
name: z.string(),
email: z.string().email(),
})
const UserPrivateSchema = UserPublicSchema.extend({
apiKey: z.string(),
sessionId: z.string(),
})
类型派生
从 schema 自动派生 TypeScript 类型:
export const TaskSchema = z.object({
id: z.string(),
command: z.string(),
status: z.enum(['running', 'completed', 'failed']),
})
export type Task = z.infer<typeof TaskSchema>
单一源头 — schema 改,类型自动跟着改。
验证错误格式化
// schemas/validate.ts
export function formatValidationError(error: z.ZodError): string {
return error.errors
.map(e => `${e.path.join('.')}: ${e.message}`)
.join('\n')
}
// 用法
try {
TaskSchema.parse(data)
} catch (e) {
if (e instanceof z.ZodError) {
console.error(formatValidationError(e))
}
}
安全解析
// 不抛异常
const result = TaskSchema.safeParse(data)
if (result.success) {
console.log(result.data)
} else {
console.log(result.error)
}
Schema Transform
const TimestampSchema = z.string()
.datetime()
.transform(s => new Date(s))
// 输入 "2026-01-01T00:00:00Z"
// 输出 Date 对象
生成 JSON Schema
Zod 4 支持转 JSON Schema:
import { toJsonSchema } from 'zod/v4'
const json = toJsonSchema(TaskSchema)
// {
// "type": "object",
// "properties": {
// "id": { "type": "string" },
// ...
// }
// }
给 LLM 的 tool schema 直接从 Zod 生成。
Schema 版本化
// 每个版本的 schema
export const TaskSchemaV1 = z.object({...})
export const TaskSchemaV2 = z.object({...})
// 当前版本
export const TaskSchema = TaskSchemaV2
配合 migrations 使用。
Schema 命名约定
// 以 Schema 结尾
const UserSchema = z.object({...})
// 类型去掉后缀
type User = z.infer<typeof UserSchema>
// 可选变体
const UserPartialSchema = UserSchema.partial()
const UserOptionalSchema = UserSchema.optional()
常用工具函数
// schemas/helpers.ts
export const nonEmptyString = z.string().min(1)
export const absolutePath = z.string().refine(
p => path.isAbsolute(p),
'Must be absolute path'
)
export const positiveInt = z.number().int().positive()
export const semver = z.string().regex(/^\d+\.\d+\.\d+/)
Schema 单元测试
test('TaskSchema validates', () => {
expect(TaskSchema.safeParse({
id: 'abc',
command: 'ls',
status: 'running'
}).success).toBe(true)
expect(TaskSchema.safeParse({
id: 'abc',
status: 'invalid'
}).success).toBe(false)
})
值得学习的点
- 集中 schema — 避免重复
- 从 schema 派生类型 — 单一源头
- Schema 是运行时合约 — 不只 TypeScript
- JSON Schema 导出 — 给 LLM 用
- Transform — schema 带转换
- safeParse — 函数式错误处理
- 组合优先 — extend/merge/pick/omit