All files / web/src/lib/arcade/validation types.ts

0% Statements 0/140
0% Branches 0/1
0% Functions 0/1
0% Lines 0/140

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141                                                                                                                                                                                                                                                                                         
/**
 * Isomorphic game validation types
 * Used on both client and server for arcade session validation
 */

import type { z } from 'zod'
import type { MemoryPairsState } from '@/arcade-games/matching/types'
import type { MemoryQuizState as SorobanQuizState } from '@/arcade-games/memory-quiz/types'

/**
 * Game name type - auto-derived from validator registry
 * @deprecated Import from '@/lib/arcade/validators' instead
 */
export type { GameName } from '../validators'

export interface ValidationResult {
  valid: boolean
  error?: string
  newState?: unknown
}

/**
 * Sentinel value for team moves where no specific player can be identified
 * Used in free-for-all games where all of a user's players act as a team
 */
export const TEAM_MOVE = '__TEAM__' as const
export type TeamMoveSentinel = typeof TEAM_MOVE

export interface GameMove {
  type: string
  playerId: string | TeamMoveSentinel // Individual player (turn-based) or __TEAM__ (free-for-all)
  userId: string // Room member/viewer who made the move
  timestamp: number
  data: unknown
}

/**
 * Re-export game-specific move types from their respective modules
 * This maintains a single source of truth (game types) while providing
 * convenient access for validation code.
 */
export type { MatchingMove } from '@/arcade-games/matching/types'
export type { MemoryQuizMove } from '@/arcade-games/memory-quiz/types'
export type { CardSortingMove } from '@/arcade-games/card-sorting/types'
export type { ComplementRaceMove } from '@/arcade-games/complement-race/types'

/**
 * Re-export game-specific state types from their respective modules
 */
export type { MatchingState } from '@/arcade-games/matching/types'
export type { MemoryQuizState } from '@/arcade-games/memory-quiz/types'
export type { CardSortingState } from '@/arcade-games/card-sorting/types'
export type { ComplementRaceState } from '@/arcade-games/complement-race/types'

// Generic game state union (for backwards compatibility)
export type GameState = MemoryPairsState | SorobanQuizState // Add other game states as union later

/**
 * Validation context for authorization checks
 */
export interface ValidationContext {
  userId?: string
  playerOwnership?: Record<string, string> // playerId -> userId mapping
}

/**
 * Options for creating practice break initial state
 */
export interface PracticeBreakOptions {
  /** Maximum duration in minutes for this game break */
  maxDurationMinutes: number
  /** Player ID who will be playing */
  playerId: string
  /** Player name for display */
  playerName?: string
  /** User/session ID who owns this player (for activeUserIds tracking) */
  userId?: string
  /** Additional players (observers joining at game start) */
  additionalPlayers?: Array<{
    playerId: string
    playerName: string
    emoji: string
    color: string
    userId: string
  }>
}

/**
 * Base validator interface that all games must implement
 */
export interface GameValidator<TState = unknown, TMove extends GameMove = GameMove> {
  /**
   * Zod schema for runtime validation of game state.
   * Used to validate state loaded from database or received from clients.
   *
   * Optional for backward compatibility - games without this will skip
   * runtime validation (less safe but allows gradual migration).
   */
  stateSchema?: z.ZodType<TState>

  /**
   * Validate a game move and return the new state if valid
   * Can be async to support lazy-loaded dependencies (e.g., ES modules)
   * @param state Current game state
   * @param move The move to validate
   * @param context Optional validation context for authorization checks
   */
  validateMove(
    state: TState,
    move: TMove,
    context?: ValidationContext
  ): ValidationResult | Promise<ValidationResult>

  /**
   * Check if the game is in a terminal state (completed)
   */
  isGameComplete(state: TState): boolean

  /**
   * Get initial state for a new game (starts in setup phase)
   */
  getInitialState(config: unknown): TState

  /**
   * Get initial state for a practice break game.
   * Unlike getInitialState, this creates a state ready to play immediately
   * (skipping the setup phase) with pre-configured settings.
   *
   * Optional: Games that don't implement this will use getInitialState
   * with the provided config and immediately send a START_GAME move.
   *
   * @param config Partial game configuration (merged with practice break defaults)
   * @param options Practice break specific options
   * @returns Game state in 'playing' phase, ready for immediate gameplay
   */
  getInitialStateForPracticeBreak?(
    config: unknown,
    options: PracticeBreakOptions
  ): TState | Promise<TState>
}