This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-MVP/packages/contracts/src/api/http.ts
T

52 lines
1.5 KiB
TypeScript

/**
* 路由契约的形状(编码规范 §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<R extends RouteContract> = z.infer<R['output']>;
export type RouteBody<R extends RouteContract> = R['body'] extends z.ZodTypeAny
? z.infer<R['body']>
: 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;
};