import * as z from "zod"; import { LOBBY_STATUSES } from "../constants.js"; import { GameQuestionSchema } from "./game.js"; export const LobbyPlayerSchema = z.object({ lobbyId: z.uuid(), userId: z.string(), score: z.number().int().min(0), user: z.object({ id: z.string(), name: z.string() }), }); export type LobbyPlayer = z.infer; export const LobbySchema = z.object({ id: z.uuid(), code: z.string().min(1).max(10), hostUserId: z.string(), status: z.enum(LOBBY_STATUSES), createdAt: z.iso.datetime(), players: z.array(LobbyPlayerSchema), }); export type Lobby = z.infer; export const JoinLobbyResponseSchema = LobbySchema; export type JoinLobbyResponse = z.infer; // ---------------------------------------------------------------------------- // WebSocket: Client → Server // ---------------------------------------------------------------------------- export const WsLobbyJoinSchema = z.object({ type: z.literal("lobby:join"), code: z.string().min(1).max(10), }); export type WsLobbyJoin = z.infer; export const WsLobbyLeaveSchema = z.object({ type: z.literal("lobby:leave"), lobbyId: z.uuid(), }); export type WsLobbyLeave = z.infer; export const WsLobbyStartSchema = z.object({ type: z.literal("lobby:start"), lobbyId: z.uuid(), }); export type WsLobbyStart = z.infer; export const WsGameReadySchema = z.object({ type: z.literal("game:ready"), lobbyId: z.uuid(), }); export type WsGameReady = z.infer; export const WsGameAnswerSchema = z.object({ type: z.literal("game:answer"), lobbyId: z.uuid(), questionId: z.uuid(), selectedOptionId: z.number().int().min(0).max(3), }); export type WsGameAnswer = z.infer; export const WsClientMessageSchema = z.discriminatedUnion("type", [ WsLobbyJoinSchema, WsLobbyLeaveSchema, WsLobbyStartSchema, WsGameAnswerSchema, WsGameReadySchema, ]); export type WsClientMessage = z.infer; // ---------------------------------------------------------------------------- // WebSocket: Server → Client // ---------------------------------------------------------------------------- export const WsLobbyStateSchema = z.object({ type: z.literal("lobby:state"), lobby: LobbySchema, }); export type WsLobbyState = z.infer; export const WsGameQuestionSchema = z.object({ type: z.literal("game:question"), question: GameQuestionSchema, questionNumber: z.number().int().min(1), totalQuestions: z.number().int().min(1), }); export type WsGameQuestion = z.infer; export const WsGameAnswerResultSchema = z.object({ type: z.literal("game:answer_result"), correctOptionId: z.number().int().min(0).max(3), results: z.array( z.object({ userId: z.string(), selectedOptionId: z.number().int().min(0).max(3).nullable(), isCorrect: z.boolean(), }), ), players: z.array(LobbyPlayerSchema), }); export type WsGameAnswerResult = z.infer; export const WsGameFinishedSchema = z.object({ type: z.literal("game:finished"), players: z.array(LobbyPlayerSchema), winnerIds: z.array(z.string()), }); export type WsGameFinished = z.infer; export const WsErrorSchema = z.object({ type: z.literal("error"), code: z.string(), message: z.string(), }); export type WsError = z.infer; export const WsServerMessageSchema = z.discriminatedUnion("type", [ WsLobbyStateSchema, WsGameQuestionSchema, WsGameAnswerResultSchema, WsGameFinishedSchema, WsErrorSchema, ]); export type WsServerMessage = z.infer;