fix: lazy-load map data in know-your-world validator
Fix production error where know-your-world game failed to load sessions due to ES module imports in CommonJS context. **Problem:** - Validator imported maps.ts at module init time - maps.ts statically imports @svg-maps/world and @svg-maps/usa (ES modules) - Server (CommonJS) cannot require() ES modules synchronously - Error: "Unexpected token 'export'" **Solution:** - Make validateMove() async (already supported by GameValidator interface) - Lazy-load getFilteredMapData() only when needed via dynamic import() - Prevents ES module loading until validator method is actually called - Client-side code continues to work normally (bundled by Next.js) - Mark know-your-world page as force-dynamic to avoid SSR issues **Changes:** - GameValidator.validateMove: Now supports Promise<ValidationResult> - KnowYourWorldValidator: Use getFilteredMapDataLazy() wrapper - session-manager: Await validator.validateMove() - know-your-world page: Add dynamic = 'force-dynamic' export Fixes the "Failed to fetch session" error for know-your-world game. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ca8e6e46ef
commit
07c25a2296
|
|
@ -57,7 +57,8 @@
|
|||
"Bash(pnpm remove:*)",
|
||||
"Bash(gh run view:*)",
|
||||
"Bash(pnpm install:*)",
|
||||
"Bash(git checkout:*)"
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(node server.js:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { knowYourWorldGame } from '@/arcade-games/know-your-world'
|
|||
|
||||
const { Provider, GameComponent } = knowYourWorldGame
|
||||
|
||||
// Opt-out of static generation due to ES module dependencies in map data
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default function KnowYourWorldPage() {
|
||||
return (
|
||||
<Provider>
|
||||
|
|
|
|||
|
|
@ -5,19 +5,27 @@ import type {
|
|||
KnowYourWorldState,
|
||||
GuessRecord,
|
||||
} from './types'
|
||||
import { getMapData, getFilteredMapData } from './maps'
|
||||
|
||||
/**
|
||||
* Lazy-load map functions to avoid importing ES modules at module init time
|
||||
* This is critical for server-side usage where ES modules can't be required
|
||||
*/
|
||||
async function getFilteredMapDataLazy(...args: Parameters<typeof import('./maps').getFilteredMapData>) {
|
||||
const { getFilteredMapData } = await import('./maps')
|
||||
return getFilteredMapData(...args)
|
||||
}
|
||||
|
||||
export class KnowYourWorldValidator
|
||||
implements GameValidator<KnowYourWorldState, KnowYourWorldMove>
|
||||
{
|
||||
validateMove(state: KnowYourWorldState, move: KnowYourWorldMove): ValidationResult {
|
||||
async validateMove(state: KnowYourWorldState, move: KnowYourWorldMove): Promise<ValidationResult> {
|
||||
switch (move.type) {
|
||||
case 'START_GAME':
|
||||
return this.validateStartGame(state, move.data)
|
||||
return await this.validateStartGame(state, move.data)
|
||||
case 'CLICK_REGION':
|
||||
return this.validateClickRegion(state, move.playerId, move.data)
|
||||
case 'NEXT_ROUND':
|
||||
return this.validateNextRound(state)
|
||||
return await this.validateNextRound(state)
|
||||
case 'END_GAME':
|
||||
return this.validateEndGame(state)
|
||||
case 'END_STUDY':
|
||||
|
|
@ -39,42 +47,21 @@ export class KnowYourWorldValidator
|
|||
}
|
||||
}
|
||||
|
||||
private validateStartGame(state: KnowYourWorldState, data: any): ValidationResult {
|
||||
private async validateStartGame(state: KnowYourWorldState, data: any): Promise<ValidationResult> {
|
||||
if (state.gamePhase !== 'setup') {
|
||||
return { valid: false, error: 'Can only start from setup phase' }
|
||||
}
|
||||
|
||||
const { activePlayers, playerMetadata, selectedMap, gameMode, difficulty } = data
|
||||
|
||||
console.log('[KnowYourWorld Validator] Starting game with:', {
|
||||
selectedMap,
|
||||
gameMode,
|
||||
difficulty,
|
||||
studyDuration: state.studyDuration,
|
||||
activePlayers: activePlayers?.length,
|
||||
})
|
||||
|
||||
if (!activePlayers || activePlayers.length === 0) {
|
||||
return { valid: false, error: 'Need at least 1 player' }
|
||||
}
|
||||
|
||||
// Get map data and shuffle regions (with continent and difficulty filters)
|
||||
const mapData = getFilteredMapData(selectedMap, state.selectedContinent, difficulty)
|
||||
console.log('[KnowYourWorld Validator] Map data loaded:', {
|
||||
map: mapData.id,
|
||||
continent: state.selectedContinent,
|
||||
difficulty,
|
||||
regionsCount: mapData.regions.length,
|
||||
regionIds: mapData.regions.map((r) => r.id).slice(0, 10),
|
||||
regionNames: mapData.regions.map((r) => r.name).slice(0, 10),
|
||||
})
|
||||
const mapData = await getFilteredMapDataLazy(selectedMap, state.selectedContinent, difficulty)
|
||||
const regionIds = mapData.regions.map((r) => r.id)
|
||||
const shuffledRegions = this.shuffleArray([...regionIds])
|
||||
console.log('[KnowYourWorld Validator] First region to find:', {
|
||||
id: shuffledRegions[0],
|
||||
name: mapData.regions.find((r) => r.id === shuffledRegions[0])?.name,
|
||||
totalShuffled: shuffledRegions.length,
|
||||
})
|
||||
|
||||
// Initialize scores and attempts
|
||||
const scores: Record<string, number> = {}
|
||||
|
|
@ -225,13 +212,13 @@ export class KnowYourWorldValidator
|
|||
}
|
||||
}
|
||||
|
||||
private validateNextRound(state: KnowYourWorldState): ValidationResult {
|
||||
private async validateNextRound(state: KnowYourWorldState): Promise<ValidationResult> {
|
||||
if (state.gamePhase !== 'results') {
|
||||
return { valid: false, error: 'Can only start next round from results' }
|
||||
}
|
||||
|
||||
// Get map data and shuffle regions (with continent and difficulty filters)
|
||||
const mapData = getFilteredMapData(state.selectedMap, state.selectedContinent, state.difficulty)
|
||||
const mapData = await getFilteredMapDataLazy(state.selectedMap, state.selectedContinent, state.difficulty)
|
||||
const regionIds = mapData.regions.map((r) => r.id)
|
||||
const shuffledRegions = this.shuffleArray([...regionIds])
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ export async function applyGameMove(
|
|||
}
|
||||
|
||||
// Validate the move with authorization context (use internal userId, not guestId)
|
||||
const validationResult = validator.validateMove(session.gameState, move, {
|
||||
const validationResult = await validator.validateMove(session.gameState, move, {
|
||||
userId: internalUserId || userId, // Use internal userId for room-based games
|
||||
playerOwnership,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -68,11 +68,12 @@ export interface ValidationContext {
|
|||
export interface GameValidator<TState = unknown, TMove extends GameMove = GameMove> {
|
||||
/**
|
||||
* 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
|
||||
validateMove(state: TState, move: TMove, context?: ValidationContext): ValidationResult | Promise<ValidationResult>
|
||||
|
||||
/**
|
||||
* Check if the game is in a terminal state (completed)
|
||||
|
|
|
|||
Loading…
Reference in New Issue