/** * 路由契约的形状(编码规范 §8.1 / §9.2)。 * * 前端只经 `shared/typed-fetch` + 本目录的路由契约与服务端交互;组件内禁止裸 fetch。 * **禁止 Hono RPC 客户端** —— 它要求前端 import 后端类型,违反 §5.1。 */ import type { z } from 'zod'; export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const; export type HttpMethod = (typeof HTTP_METHODS)[number]; /** * 一条路由契约。`path` 用 `:name` 表示路径参数,与 `params` 的键一一对应。 * body 只在 POST/PATCH 上有意义;GET 用 query。 */ export type RouteContract< TParams extends z.ZodTypeAny = z.ZodTypeAny, TQuery extends z.ZodTypeAny = z.ZodTypeAny, TBody extends z.ZodTypeAny = z.ZodTypeAny, TOutput extends z.ZodTypeAny = z.ZodTypeAny, > = { readonly method: HttpMethod; readonly path: string; readonly params?: TParams; readonly query?: TQuery; readonly body?: TBody; readonly output: TOutput; }; export type RouteOutput = z.infer; export type RouteBody = R['body'] extends z.ZodTypeAny ? z.infer : never; /** 结构化错误:前端按 code 分辨,不解析文案 */ export const API_ERROR_CODES = [ 'unauthenticated', 'forbidden', 'not-found', 'validation-failed', 'conflict', 'precondition-failed', 'internal', ] as const; export type ApiErrorCode = (typeof API_ERROR_CODES)[number]; export type ApiError = { readonly code: ApiErrorCode; readonly message: string; readonly detail?: unknown; };