/** * WebSocket Event Types * * TypeScript definitions for all Socket.io events between client and server. * Ensures type safety for real-time communication. * * Backend Reference: app/websocket/handlers.py */ import type { GameState, PlayResult, DecisionPrompt, RollData, DefensiveDecision, OffensiveDecision, ManualOutcomeSubmission, } from './game' import type { SubstitutionRequest, SubstitutionResult, SubstitutionError, LineupDataResponse, } from './player' /** * WebSocket connection auth */ export interface SocketAuth { token: string } /** * Client → Server Events (what client can emit) */ export interface ClientToServerEvents { // Connection management join_game: (data: JoinGameRequest) => void leave_game: (data: LeaveGameRequest) => void heartbeat: () => void // Strategic decisions submit_defensive_decision: (data: DefensiveDecisionRequest) => void submit_offensive_decision: (data: OffensiveDecisionRequest) => void // Manual outcome workflow roll_dice: (data: RollDiceRequest) => void submit_manual_outcome: (data: SubmitManualOutcomeRequest) => void // Substitutions request_pinch_hitter: (data: PinchHitterRequest) => void request_defensive_replacement: (data: DefensiveReplacementRequest) => void request_pitching_change: (data: PitchingChangeRequest) => void // Data requests get_lineup: (data: GetLineupRequest) => void get_box_score: (data: GetBoxScoreRequest) => void request_game_state: (data: RequestGameStateRequest) => void } /** * Server → Client Events (what client can receive) */ export interface ServerToClientEvents { // Connection events connected: (data: ConnectedEvent) => void game_joined: (data: GameJoinedEvent) => void user_connected: (data: UserConnectedEvent) => void user_disconnected: (data: UserDisconnectedEvent) => void heartbeat_ack: () => void // Decision events defensive_decision_submitted: (data: DefensiveDecisionSubmittedEvent) => void offensive_decision_submitted: (data: OffensiveDecisionSubmittedEvent) => void decision_required: (data: DecisionPrompt) => void // Game state events game_state: (data: GameState) => void game_state_update: (data: GameState) => void game_state_sync: (data: GameStateSyncEvent) => void play_completed: (data: PlayResult) => void inning_change: (data: InningChangeEvent) => void game_ended: (data: GameEndedEvent) => void // Manual workflow events dice_rolled: (data: DiceRolledEvent) => void outcome_accepted: (data: OutcomeAcceptedEvent) => void // Substitution events player_substituted: (data: SubstitutionResult) => void substitution_confirmed: (data: SubstitutionConfirmedEvent) => void // Data responses lineup_data: (data: LineupDataResponse) => void box_score_data: (data: BoxScoreDataEvent) => void // Error events error: (data: ErrorEvent) => void outcome_rejected: (data: OutcomeRejectedEvent) => void substitution_error: (data: SubstitutionError) => void invalid_action: (data: InvalidActionEvent) => void connection_error: (data: ConnectionErrorEvent) => void } // ============================================================================ // Client → Server Request Types // ============================================================================ export interface JoinGameRequest { game_id: string role: 'player' | 'spectator' } export interface LeaveGameRequest { game_id: string } export interface DefensiveDecisionRequest { game_id: string infield_depth: DefensiveDecision['infield_depth'] outfield_depth: DefensiveDecision['outfield_depth'] hold_runners: number[] } export interface OffensiveDecisionRequest { game_id: string action: OffensiveDecision['action'] steal_attempts: number[] } export interface RollDiceRequest { game_id: string } export interface SubmitManualOutcomeRequest { game_id: string outcome: string hit_location?: string } export interface PinchHitterRequest { game_id: string player_out_lineup_id: number player_in_card_id: number team_id: number } export interface DefensiveReplacementRequest { game_id: string player_out_lineup_id: number player_in_card_id: number new_position: string team_id: number } export interface PitchingChangeRequest { game_id: string player_out_lineup_id: number player_in_card_id: number team_id: number } export interface GetLineupRequest { game_id: string team_id: number } export interface GetBoxScoreRequest { game_id: string } export interface RequestGameStateRequest { game_id: string } // ============================================================================ // Server → Client Event Types // ============================================================================ export interface ConnectedEvent { user_id: string } export interface GameJoinedEvent { game_id: string role: 'player' | 'spectator' } export interface UserConnectedEvent { user_id: string game_id: string } export interface UserDisconnectedEvent { user_id: string game_id: string } export interface DefensiveDecisionSubmittedEvent { game_id: string decision: DefensiveDecision pending_decision: 'offensive' | 'resolution' | null } export interface OffensiveDecisionSubmittedEvent { game_id: string decision: OffensiveDecision pending_decision: 'resolution' | null } export interface DiceRolledEvent { game_id: string roll_id: string d6_one: number d6_two_total: number chaos_d20: number resolution_d20: number check_wild_pitch: boolean check_passed_ball: boolean timestamp: string message: string } export interface OutcomeAcceptedEvent { outcome: string hit_location?: string } export interface InningChangeEvent { inning: number half: 'top' | 'bottom' } export interface GameEndedEvent { game_id: string winner_team_id: number final_score: { home: number away: number } completed_at: string } export interface SubstitutionConfirmedEvent { type: string new_lineup_id: number message: string } export interface BoxScoreDataEvent { game_id: string box_score: { game_stats: { home_runs: number away_runs: number home_hits: number away_hits: number home_errors: number away_errors: number linescore_home: number[] linescore_away: number[] } batting_stats: BattingStatLine[] pitching_stats: PitchingStatLine[] } } export interface BattingStatLine { lineup_id: number player_card_id: number pa: number ab: number run: number hit: number rbi: number double: number triple: number hr: number bb: number so: number hbp: number sac: number sb: number cs: number gidp: number } export interface PitchingStatLine { lineup_id: number player_card_id: number batters_faced: number ip: number hit_allowed: number run_allowed: number erun: number bb: number so: number hbp: number hr_allowed: number wp: number } export interface GameStateSyncEvent { state: GameState recent_plays: PlayResult[] timestamp: string } export interface ErrorEvent { message: string code?: string field?: string hint?: string } export interface OutcomeRejectedEvent { message: string field: string errors?: Array<{ loc: string[] msg: string type: string }> } export interface InvalidActionEvent { action: string reason: string } export interface ConnectionErrorEvent { error: string details?: string } /** * Type-safe Socket.io instance */ export interface TypedSocket { on( event: K, listener: ServerToClientEvents[K] ): void emit( event: K, ...args: Parameters ): void off( event: K, listener?: ServerToClientEvents[K] ): void connect(): void disconnect(): void readonly connected: boolean readonly id: string }