Files
OJ2/apps/web/src/shared/api.ts
T
xuyueandClaude Opus 5.5 83b9d23c68 perf(前端): zod 移出首屏包,首屏 JS 721KB → 594KB(gzip 237 → 199KB)
首屏包里带着整个 zod 运行时(~100KB),来路有三条,缺一条都挪不出去(逐条撤回实测过):

- 契约包没有 sideEffects: false,入口从桶里取任何一个值都会把所有 schema 模块带上。
  契约包里没有顶层副作用(核过),加上之后 rolldown 才能按导出摇树
- roles.ts 里的 USER_TYPE / toAdminType 是入口要的,同文件却定义了两个 z.enum。
  schema 挪到 admin.ts(唯一用处),roles.ts 文件头写明不许 import zod
- 启动就要调的 getWebsiteConfig / getAILoginSummary 在 oj/api.ts 里,而那个文件同步
  import 了题目详情、提交详情两个 schema。这两个函数挪到 shared/api.ts;那两道
  闸门保持同步 —— submissionDetailSchema 有 .catch(),放行原文和解析结果不等价
- /me 的契约闸门改成 contractDeferred:先放行原文,schema 按需加载后在后台核、只记
  日志。userProfileSchema 没有 default / transform,两者等价。动态 import 的目标是
  单独的 shared/profileSchema.ts —— 直接 import 契约桶会让 rolldown 重排共享 chunk,
  实测首屏反而涨到 1811KB

浏览器实跑核过:登录后 profile 正常加载、profileSchema 按需拉取、无控制台报错;
故意喂一份不符的数据,后台闸门照常打出 [契约] 分歧日志。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:23:31 +08:00

63 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { LoginSummary, Quote, WebsiteConfig } from "@oj2/contract"
import api from "utils/api"
import { contractDeferred } from "utils/contract"
import type { Profile, Tag } from "utils/types"
export function login(data: { username: string; password: string }) {
return api.post("auth/login", data)
}
export function signup(data: {
username: string
email: string
password: string
}) {
return api.post("users", data)
}
export function logout() {
return api.delete("auth/session")
}
export async function getProfile(
username: string = "",
): Promise<Profile | null> {
const endpoint = username ? `profiles/${encodeURIComponent(username)}` : "me"
const response = await api.get<unknown>(endpoint)
if (response === null) return null
// 形状与契约一致,不再逐字段搬运。走契约闸门而不是裸 parse():原来这里是
// `userProfileSchema.parse(response) as Profile`,`as` 把校验结果又断言回本地
// 类型(Profile 把 user 收窄成 SessionUser、acmProblemsStatus 收窄成具体形状),
// 形状对不上时页面白屏。现在记一条分歧日志后放行原始数据。
// 用 deferred 版:启动就要拉 /me,同步校验会把 zod 整个打进首屏包
return contractDeferred(
"GET /profiles/:username",
() => import("./profileSchema").then((m) => m.userProfileSchema),
response,
)
}
export function getProblemTagList() {
return api.get<Tag[]>("problem-tags")
}
export function getHitokoto() {
return api.get<Quote>("quotes/random")
}
export function getClassUsernames(classroom: string) {
return api.get<string[]>(`classes/${encodeURIComponent(classroom)}/usernames`)
}
// 下面两个是启动就要调的(config / loginSummary 两个 store),所以放在 shared 而不是
// oj/api.ts:oj/api.ts 同步 import 了题目详情、提交详情两个 schema(它们的契约闸门必须
// 同步,submissionDetailSchema 里有 `.catch()`,放行原文和解析结果不等价),入口一旦
// 引到它,zod 整个就进了首屏包。
export function getWebsiteConfig() {
return api.get<WebsiteConfig>("site")
}
export function getAILoginSummary() {
return api.get<LoginSummary>("ai/login-summary")
}