refactor: code review improvements
- Add 30s timeout to Stockfish evaluation to prevent hanging promises - Add FEN validation and depth cap (max 30) to Stockfish API route - Create ErrorBoundary component with specialized fallbacks for chess game and tutor - Extract useChessSounds hook for better audio management - Add React.memo to EvaluationBar and CapturedPieces for performance - Add useMemo to CapturedPieces for sorted pieces calculation - Improve tacticDetection to return empty array instead of "none" type - Add filterMeaningfulTactics and hasTactics helper functions - Translate hardcoded UI strings (stockfishLevel, download, evalChange) - Update translations for EN, DE, FR, IT, PL - Add uuid to Jest transformIgnorePatterns for ESM compatibility - Update tests to use new filterMeaningfulTactics function
This commit is contained in:
@@ -31,10 +31,15 @@ jest.mock("@/lib/stockfish", () => {
|
||||
jest.mock("@/lib/tacticDetection", () => {
|
||||
const detectMissedTactics = jest.fn();
|
||||
const uciToSan = jest.fn();
|
||||
const filterMeaningfulTactics = jest.fn((tactics) => {
|
||||
if (!tactics) return [];
|
||||
return tactics.filter((t: { tactic_type: string }) => t.tactic_type !== "none");
|
||||
});
|
||||
return {
|
||||
__esModule: true,
|
||||
detectMissedTactics,
|
||||
uciToSan,
|
||||
filterMeaningfulTactics,
|
||||
__mock: { detectMissedTactics, uciToSan },
|
||||
};
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useTranslation } from "@/lib/i18n/useTranslation";
|
||||
import { Personality, PERSONALITIES } from "@/lib/personalities";
|
||||
import { Stockfish, StockfishEvaluation } from "@/lib/stockfish";
|
||||
import { detectChessFormat, ChessFormat } from "@/lib/chessFormatDetector";
|
||||
import { detectMissedTactics, DetectedTactic, uciToSan } from "@/lib/tacticDetection";
|
||||
import { detectMissedTactics, DetectedTactic, uciToSan, filterMeaningfulTactics } from "@/lib/tacticDetection";
|
||||
import { lookupPossibleOpenings, buildMoveSequenceFromSteps, OpeningMetadata } from "@/lib/openings";
|
||||
import { getGenAIModel } from "@/lib/gemini";
|
||||
import { ChatSession } from "@google/generative-ai";
|
||||
@@ -326,8 +326,7 @@ IMPORTANT:
|
||||
const evalBefore = details.evalBefore!.score / 100;
|
||||
const evalAfter = details.evalAfter!.score / 100;
|
||||
const mateInfo = details.evalAfter!.mate !== null ? `Mate in ${details.evalAfter!.mate}` : "No mate detected";
|
||||
const tactics = (details.missedTactics || [])
|
||||
.filter(t => t.tactic_type !== "none")
|
||||
const tactics = filterMeaningfulTactics(details.missedTactics)
|
||||
.map(t => `${t.tactic_type}${t.material_delta ? ` (~${(t.material_delta / 100).toFixed(1)} pawns)` : ""}`)
|
||||
.join("; ") || "None";
|
||||
|
||||
@@ -406,7 +405,7 @@ INSTRUCTIONS:
|
||||
};
|
||||
|
||||
const currentDetails = currentIndex > 0 ? stepDetails[currentIndex] : undefined;
|
||||
const tacticSummary = (currentDetails?.missedTactics || []).filter(t => t.tactic_type !== "none");
|
||||
const tacticSummary = filterMeaningfulTactics(currentDetails?.missedTactics);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-gray-100 dark:bg-gray-900">
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { evaluateStockfish } from "@/lib/server/stockfishEngine";
|
||||
import { StockfishEvaluation } from "@/lib/stockfish";
|
||||
import { Chess } from "chess.js";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
/**
|
||||
* Validates a FEN string by attempting to create a Chess instance
|
||||
*/
|
||||
function isValidFEN(fen: string): boolean {
|
||||
try {
|
||||
new Chess(fen);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
@@ -13,6 +26,11 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: "Missing or invalid FEN" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Validate FEN string format and chess position validity
|
||||
if (!isValidFEN(fen)) {
|
||||
return NextResponse.json({ error: "Invalid FEN: position is not a valid chess position" }, { status: 400 });
|
||||
}
|
||||
|
||||
const parsedDepth = Number(depth);
|
||||
const parsedMultiPV = Number(multiPV);
|
||||
|
||||
@@ -20,11 +38,15 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: "Depth must be a positive number" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Cap depth to prevent excessive computation
|
||||
const maxDepth = 30;
|
||||
const safeDepth = Math.min(parsedDepth, maxDepth);
|
||||
|
||||
if (!Number.isFinite(parsedMultiPV) || parsedMultiPV <= 0) {
|
||||
return NextResponse.json({ error: "multiPV must be a positive number" }, { status: 400 });
|
||||
}
|
||||
|
||||
const evaluation: StockfishEvaluation = await evaluateStockfish(fen, parsedDepth, parsedMultiPV);
|
||||
const evaluation: StockfishEvaluation = await evaluateStockfish(fen, safeDepth, parsedMultiPV);
|
||||
return NextResponse.json({ evaluation });
|
||||
} catch (error) {
|
||||
console.error("Stockfish API error", error);
|
||||
|
||||
Reference in New Issue
Block a user