import { z } from "zod"; /** 本地实体 ID 一律 UUID */ export const Id = z.uuid(); export type Id = z.infer; /** ISO 8601 时间串 */ export const Timestamp = z.iso.datetime({ offset: true }); export type Timestamp = z.infer; /** 游标分页请求:cursor 为不透明字符串,由服务端编码 */ export const CursorQuery = z.object({ cursor: z.string().min(1).optional(), limit: z.coerce.number().int().min(1).max(200).default(50), }); export type CursorQuery = z.infer; /** 游标分页结果:nextCursor 为 null 表示已到末尾 */ export const cursorPage = (item: T) => z.object({ items: z.array(item), nextCursor: z.string().nullable(), }); export type CursorPage = { items: T[]; nextCursor: string | null }; /** 错误码表:新增错误码只在这里加 */ export const ApiErrorCode = z.enum([ "bad_request", "validation_failed", "unauthorized", "forbidden", "not_found", "conflict", "precondition_failed", "rate_limited", "automation_disabled", "upstream_unavailable", "internal", ]); export type ApiErrorCode = z.infer; /** 错误码对应的 HTTP 状态 */ export const API_ERROR_STATUS = { bad_request: 400, validation_failed: 422, unauthorized: 401, forbidden: 403, not_found: 404, conflict: 409, precondition_failed: 412, rate_limited: 429, automation_disabled: 423, upstream_unavailable: 502, internal: 500, } as const satisfies Record; export const ApiError = z.object({ code: ApiErrorCode, message: z.string(), }); export type ApiError = z.infer; /** 统一响应信封 */ export const apiResponse = (data: T) => z.object({ ok: z.boolean(), data: data.optional(), error: ApiError.optional(), }); export type ApiResponse = | { ok: true; data: T; error?: undefined } | { ok: false; data?: undefined; error: ApiError }; /** 没有 data 的失败响应 schema(OpenAPI 错误分支用) */ export const ApiErrorResponse = z.object({ ok: z.literal(false), error: ApiError, }); /** API Key 权限范围 */ export const ApiScope = z.enum(["read", "organize.read", "organize.execute", "admin"]); export type ApiScope = z.infer; /** 健康检查 */ export const HealthStatus = z.object({ status: z.enum(["ok", "degraded"]), version: z.string(), role: z.enum(["api", "worker", "all"]), uptimeSec: z.number(), db: z.enum(["ok", "down", "unknown"]), time: Timestamp, }); export type HealthStatus = z.infer; /** 日期(无时区),如 air_date:YYYY-MM-DD */ export const DateOnly = z.iso.date(); export type DateOnly = z.infer; /** JSONB 对象列(证据、规则参数、原文快照等)的 API 表示 */ export const JsonObject = z.record(z.string(), z.unknown()); export type JsonObject = z.infer; /** 字节数、计数等非负整数(DB 为 bigint,客户端按 number 解析,上限 2^53) */ export const NonNegInt = z.number().int().nonnegative(); /** 操作者类型:与 DB 中 actor_type 列一致 */ export const ActorType = z.enum(["user", "agent", "system", "api_key"]); export type ActorType = z.infer; /** 实体行的公共字段 */ export const EntityBase = z.object({ id: Id, createdAt: Timestamp, updatedAt: Timestamp, }); /** 所有实体 Create 输入都要剔除的服务端字段 */ export const SERVER_MANAGED = { id: true, createdAt: true, updatedAt: true } as const; /** 路径参数 `{id}` */ export const IdParam = z.object({ id: Id }); export type IdParam = z.infer;