import type { CatalogEntry, PublicUser } from '@bg/shared/lite'; export class ApiError extends Error { constructor( message: string, readonly status: number, ) { super(message); } } async function call(method: string, path: string, body?: unknown): Promise { let res: Response; try { res = await fetch(path, { method, credentials: 'same-origin', headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined, body: body !== undefined ? JSON.stringify(body) : undefined, }); } catch { throw new ApiError('인터넷 연결을 확인해 주세요.', 0); } const data = (await res.json().catch(() => ({}))) as { error?: string } & T; if (!res.ok) throw new ApiError(data.error ?? '잠시 후 다시 시도해 주세요.', res.status); return data; } export interface Settings { fontScale?: 'normal' | 'large' | 'xlarge'; theme?: 'auto' | 'light' | 'dark'; sound?: boolean; vibrate?: boolean; colorBlind?: boolean; tapConfirm?: boolean; } export interface MeResponse { me: PublicUser | null; settings?: Settings; useAvatar?: boolean; hasDiscordAvatar?: boolean; activeRoom?: string | null; } export interface RoomPreview { code: string; codeLabel: string; gameId: string; host: string; seated: number; maxPlayers: number; status: string; } export const api = { config: () => call<{ discord: boolean; catalog: CatalogEntry[] }>('GET', '/api/config'), me: () => call('GET', '/api/me'), guest: (nickname: string) => call<{ me: PublicUser }>('POST', '/api/auth/guest', { nickname }), updateMe: (patch: { nickname?: string; useAvatar?: boolean; settings?: Settings }) => call<{ me: PublicUser }>('PATCH', '/api/me', patch), logout: () => call<{ ok: true }>('POST', '/api/auth/logout', {}), logoutAll: () => call<{ ok: true }>('POST', '/api/auth/logout-all', {}), deleteMe: () => call<{ ok: true }>('DELETE', '/api/me', { confirm: '탈퇴' }), stats: () => call<{ stats: { gameId: string; played: number; wins: number; draws: number }[] }>('GET', '/api/me/stats'), createRoom: (gameId: string, visibility?: 'private' | 'public') => call<{ code: string }>('POST', '/api/rooms', { gameId, visibility }), quickPlay: (gameId: string) => call<{ code: string }>('POST', `/api/quick/${gameId}`, {}), room: (code: string) => call('GET', `/api/rooms/${code}`), publicRooms: (gameId: string) => call<{ rooms: { code: string; host: string; seated: number; max: number }[] }>('GET', `/api/games/${gameId}/rooms`), }; export const discordLoginUrl = (next: string) => `/api/auth/discord/start?next=${encodeURIComponent(next)}`;