Files
UHD_Media_Tool/apps/web/app/api/parse.ts
T
rootandClaude Opus 5.5 52402864d6 style(copy): punctuation rules and site-wide fix with a CI check (QA U11)
- glossary: 10 punctuation rules (full-width in Chinese, CJK/Latin spacing,
  full-width parens around Chinese, single ellipsis, 「」 quotes, period rules)
- scripts/check-copy-punctuation.mjs: checks string literals, templates and JSX
  text (not comments, types, keys, match/compare strings); --fix, --strict,
  --by-file; wired into deno task ci as check:copy in warn mode
- --fix applied to web, server and contracts; files other lanes were editing
  (subscriptions, sites, browser, agent, auth/account, inventory) left for them
- tests updated for the changed messages

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 20:09:29 +08:00

40 lines
1.7 KiB
TypeScript

import type { ApiErrorCode } from "@umt/contracts";
import type { z } from "zod";
import { ApiRequestError, apiFetch, type FetcherArg } from "~/lib/api.ts";
export { ApiRequestError };
/** 请求并用 contracts 的 zod schema 校验 data;失败抛 ApiRequestError(给 react-query 的 error 态) */
export async function apiParsed<S extends z.ZodType>(schema: S, path: string, arg?: FetcherArg): Promise<z.infer<S>> {
const r = await apiFetch<unknown>(path, arg);
if (!r.ok) throw new ApiRequestError(r.error.code, r.error.message);
const parsed = schema.safeParse(r.data);
if (!parsed.success) {
const issue = parsed.error.issues[0];
throw new ApiRequestError(
"internal" satisfies ApiErrorCode,
`响应格式不符合接口定义:${issue ? `${issue.path.join(".")} ${issue.message}` : "未知字段"}`,
);
}
return parsed.data;
}
/** 变更请求:返回 data,失败抛错(给 useMutation,由 onError 统一 toast) */
export async function apiMutate<T = unknown>(path: string, arg: FetcherArg = {}): Promise<T> {
const r = await apiFetch<T>(path, { method: "POST", ...arg });
if (!r.ok) throw new ApiRequestError(r.error.code, r.error.message);
return r.data;
}
export const errorCode = (err: unknown): string | null => (err instanceof ApiRequestError ? err.code : null);
/** 端点是否不存在(后端尚未提供) */
export const isMissingEndpoint = (err: unknown) => errorCode(err) === "not_found";
/** 查询字符串:去掉空值 */
export function cleanParams(p: Record<string, string | number | boolean | undefined | null>) {
const out: Record<string, string | number | boolean> = {};
for (const [k, v] of Object.entries(p)) if (v !== undefined && v !== null && v !== "") out[k] = v;
return out;
}