diff --git a/.eslintrc.json b/.eslintrc.json index 14deee7..d6e85eb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,6 +11,7 @@ "plugin:import/typescript", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", + "plugin:deprecation/recommended", "next", "prettier" ], diff --git a/cdk/app-stack.ts b/cdk/app-stack.ts index 72ff225..a264548 100644 --- a/cdk/app-stack.ts +++ b/cdk/app-stack.ts @@ -106,6 +106,7 @@ export class AppStack extends cdk.Stack { domainName, }); + // eslint-disable-next-line const certificate = new DnsValidatedCertificate(this, "Certificate", { domainName, hostedZone, diff --git a/package-lock.json b/package-lock.json index 6223079..c4f98dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@mui/material": "^6.3.0", "@mui/x-data-grid": "^7.23.5", "@sentry/nextjs": "^8.47.0", - "chess.js": "^1.0.0-beta.8", + "chess.js": "^1.2.0", "firebase": "^11.1.0", "idb": "^8.0.1", "jotai": "^2.11.0", @@ -39,6 +39,7 @@ "eslint": "^8.57.1", "eslint-config-next": "^15.1.3", "eslint-config-prettier": "^8.10.0", + "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.2.1", "typescript": "^5.7.2" @@ -4091,6 +4092,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/type-utils": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz", @@ -4213,6 +4232,75 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.2.tgz", @@ -4353,6 +4441,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", @@ -4733,6 +4839,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -5504,9 +5620,10 @@ } }, "node_modules/chess.js": { - "version": "1.0.0-beta.8", - "resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.0.0-beta.8.tgz", - "integrity": "sha512-UngzUMXmexcQaQA/UEJuJj5vatEy34awYMD5YMOp/FW3HM7lqspp7ymYs5JAmquDq0WROtURRfSffoa/vrCCyw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.2.0.tgz", + "integrity": "sha512-QWdfpAkMslQ48hDDoEzEko6GbYTAKKx4UdXAPEF8nFnAuqp+woyL8LHSbziRP8u1aMAbnlOF0YkXF4ui0Nsubg==", + "license": "BSD-2-Clause" }, "node_modules/chokidar": { "version": "3.6.0", @@ -5954,6 +6071,19 @@ "node": ">=8" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dnd-core": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", @@ -6408,6 +6538,45 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-deprecation": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-3.0.0.tgz", + "integrity": "sha512-JuVLdNg/uf0Adjg2tpTyYoYaMbwQNn/c78P1HcccokvhtRphgnRjZDKmhlxbxYptppex03zO76f97DD/yQHv7A==", + "dev": true, + "license": "LGPL-3.0-or-later", + "dependencies": { + "@typescript-eslint/utils": "^7.0.0", + "ts-api-utils": "^1.3.0", + "tslib": "^2.3.1" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "typescript": "^4.2.4 || ^5.0.0" + } + }, + "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, "node_modules/eslint-plugin-import": { "version": "2.31.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", @@ -7204,6 +7373,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -9576,6 +9766,16 @@ "license": "MIT", "optional": true }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", diff --git a/package.json b/package.json index 4fd2d9c..75acf71 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@mui/material": "^6.3.0", "@mui/x-data-grid": "^7.23.5", "@sentry/nextjs": "^8.47.0", - "chess.js": "^1.0.0-beta.8", + "chess.js": "^1.2.0", "firebase": "^11.1.0", "idb": "^8.0.1", "jotai": "^2.11.0", @@ -35,14 +35,15 @@ "@types/react-dom": "^18.3.5", "@typescript-eslint/eslint-plugin": "^8.18.2", "@typescript-eslint/parser": "^8.18.2", + "aws-cdk": "^2.1007.0", + "aws-cdk-lib": "^2.186.0", + "constructs": "^10.0.0", "eslint": "^8.57.1", "eslint-config-next": "^15.1.3", "eslint-config-prettier": "^8.10.0", + "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.2.1", - "typescript": "^5.7.2", - "aws-cdk": "^2.1007.0", - "aws-cdk-lib": "^2.186.0", - "constructs": "^10.0.0" + "typescript": "^5.7.2" } } diff --git a/src/hooks/useAtomLocalStorage.ts b/src/hooks/useAtomLocalStorage.ts index b24a273..444261a 100644 --- a/src/hooks/useAtomLocalStorage.ts +++ b/src/hooks/useAtomLocalStorage.ts @@ -9,11 +9,11 @@ export function useAtomLocalStorage( const [storedValue, setStoredValue] = useAtom(atom); useEffect(() => { - const item = window.localStorage.getItem(key); - if (item) { - setStoredValue(parseJSON(item)); - } setKeyTemp(key); + const item = window.localStorage.getItem(key); + if (!item) return; + const value = parseJSON(item); + if (value) setStoredValue(value); }, [key, setStoredValue]); useEffect(() => { @@ -24,6 +24,6 @@ export function useAtomLocalStorage( return [storedValue, setStoredValue]; } -function parseJSON(value: string): T { +function parseJSON(value: string): T | undefined { return value === "undefined" ? undefined : JSON.parse(value); } diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts index bc03e47..13c3a96 100644 --- a/src/hooks/useLocalStorage.ts +++ b/src/hooks/useLocalStorage.ts @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useState } from "react"; type SetValue = Dispatch>; -export function useLocalStorage( +export function useLocalStorage( key: string, initialValue: T ): [T | null, SetValue] { @@ -11,10 +11,13 @@ export function useLocalStorage( useEffect(() => { const item = window.localStorage.getItem(key); if (item) { - setStoredValue(parseJSON(item)); - } else { - setStoredValue(initialValue); + const value = parseJSON(item); + if (value) { + setStoredValue(value); + return; + } } + setStoredValue(initialValue); }, [key, initialValue]); const setValue: SetValue = (value) => { @@ -28,6 +31,6 @@ export function useLocalStorage( return [storedValue, setValue]; } -function parseJSON(value: string): T { +function parseJSON(value: string): T | undefined { return value === "undefined" ? undefined : JSON.parse(value); } diff --git a/src/hooks/usePlayerNames.ts b/src/hooks/usePlayerNames.ts index 31cb6ad..3b33250 100644 --- a/src/hooks/usePlayerNames.ts +++ b/src/hooks/usePlayerNames.ts @@ -5,17 +5,13 @@ import { useGameDatabase } from "./useGameDatabase"; export const usePlayersNames = (gameAtom: PrimitiveAtom) => { const game = useAtomValue(gameAtom); const { gameFromUrl } = useGameDatabase(); + const headers = game.getHeaders(); - const whiteName = - gameFromUrl?.white?.name || game.header()["White"] || "White"; - const blackName = - gameFromUrl?.black?.name || game.header()["Black"] || "Black"; + const whiteName = gameFromUrl?.white?.name || headers.White || "White"; + const blackName = gameFromUrl?.black?.name || headers.Black || "Black"; - const whiteElo = - gameFromUrl?.white?.rating || game.header()["WhiteElo"] || undefined; - - const blackElo = - gameFromUrl?.black?.rating || game.header()["BlackElo"] || undefined; + const whiteElo = gameFromUrl?.white?.rating || headers.WhiteElo || undefined; + const blackElo = gameFromUrl?.black?.rating || headers.BlackElo || undefined; return { whiteName, diff --git a/src/lib/chess.ts b/src/lib/chess.ts index 5e1d7b6..3376124 100644 --- a/src/lib/chess.ts +++ b/src/lib/chess.ts @@ -25,7 +25,7 @@ export const getGameFromPgn = (pgn: string): Chess => { }; export const formatGameToDatabase = (game: Chess): Omit => { - const headers: Record = game.header(); + const headers: Record = game.getHeaders(); return { pgn: game.pgn(), @@ -56,24 +56,24 @@ export const setGameHeaders = ( game: Chess, params: { whiteName?: string; blackName?: string; resigned?: Color } = {} ): Chess => { - game.header("Event", "Chesskit Game"); - game.header("Site", "Chesskit"); - game.header( + game.setHeader("Event", "Chesskit Game"); + game.setHeader("Site", "Chesskit"); + game.setHeader( "Date", new Date().toISOString().split("T")[0].replaceAll("-", ".") ); const { whiteName, blackName, resigned } = params; - if (whiteName) game.header("White", whiteName); - if (blackName) game.header("Black", blackName); + if (whiteName) game.setHeader("White", whiteName); + if (blackName) game.setHeader("Black", blackName); - const whiteNameToUse = game.header().White || "White"; - const blackNameToUse = game.header().Black || "Black"; + const whiteNameToUse = game.getHeaders().White || "White"; + const blackNameToUse = game.getHeaders().Black || "Black"; if (resigned) { - game.header("Result", resigned === "w" ? "0-1" : "1-0"); - game.header( + game.setHeader("Result", resigned === "w" ? "0-1" : "1-0"); + game.setHeader( "Termination", `${resigned === "w" ? blackNameToUse : whiteNameToUse} won by resignation` ); @@ -82,8 +82,8 @@ export const setGameHeaders = ( if (!game.isGameOver()) return game; if (game.isCheckmate()) { - game.header("Result", game.turn() === "w" ? "0-1" : "1-0"); - game.header( + game.setHeader("Result", game.turn() === "w" ? "0-1" : "1-0"); + game.setHeader( "Termination", `${ game.turn() === "w" ? blackNameToUse : whiteNameToUse @@ -92,18 +92,18 @@ export const setGameHeaders = ( } if (game.isInsufficientMaterial()) { - game.header("Result", "1/2-1/2"); - game.header("Termination", "Draw by insufficient material"); + game.setHeader("Result", "1/2-1/2"); + game.setHeader("Termination", "Draw by insufficient material"); } if (game.isStalemate()) { - game.header("Result", "1/2-1/2"); - game.header("Termination", "Draw by stalemate"); + game.setHeader("Result", "1/2-1/2"); + game.setHeader("Termination", "Draw by stalemate"); } if (game.isThreefoldRepetition()) { - game.header("Result", "1/2-1/2"); - game.header("Termination", "Draw by threefold repetition"); + game.setHeader("Result", "1/2-1/2"); + game.setHeader("Termination", "Draw by threefold repetition"); } return game; diff --git a/src/lib/sounds.ts b/src/lib/sounds.ts index 85192c7..7432362 100644 --- a/src/lib/sounds.ts +++ b/src/lib/sounds.ts @@ -34,7 +34,7 @@ export const playSoundFromMove = async (move: Move | null) => { playPromoteSound(); } else if (move.captured) { playCaptureSound(); - } else if (move.flags.includes("k") || move.flags.includes("q")) { + } else if (move.isKingsideCastle() || move.isQueensideCastle()) { playCastleSound(); } else { playMoveSound(); diff --git a/src/sections/analysis/panelHeader/gamePanel.tsx b/src/sections/analysis/panelHeader/gamePanel.tsx index 00c0843..c2f94e0 100644 --- a/src/sections/analysis/panelHeader/gamePanel.tsx +++ b/src/sections/analysis/panelHeader/gamePanel.tsx @@ -6,17 +6,18 @@ import { gameAtom } from "../states"; export default function GamePanel() { const { gameFromUrl } = useGameDatabase(); const game = useAtomValue(gameAtom); + const gameHeaders = game.getHeaders(); - const hasGameInfo = gameFromUrl !== undefined || !!game.header().White; + const hasGameInfo = gameFromUrl !== undefined || !!gameHeaders.White; if (!hasGameInfo) return null; const termination = - gameFromUrl?.termination || game.header().Termination || "?"; + gameFromUrl?.termination || gameHeaders.Termination || "?"; const result = termination.split(" ").length > 2 ? termination - : gameFromUrl?.result || game.header().Result || "?"; + : gameFromUrl?.result || gameHeaders.Result || "?"; return ( - Site : {gameFromUrl?.site || game.header().Site || "?"} + Site : {gameFromUrl?.site || gameHeaders.Site || "?"} - Date : {gameFromUrl?.date || game.header().Date || "?"} + Date : {gameFromUrl?.date || gameHeaders.Date || "?"} diff --git a/src/sections/analysis/panelHeader/loadGame.tsx b/src/sections/analysis/panelHeader/loadGame.tsx index 58577e1..fd5d93e 100644 --- a/src/sections/analysis/panelHeader/loadGame.tsx +++ b/src/sections/analysis/panelHeader/loadGame.tsx @@ -49,7 +49,7 @@ export default function LoadGame() { loadGame(); }, [gameFromUrl, game, resetAndSetGamePgn, setEval]); - const isGameLoaded = gameFromUrl !== undefined || !!game.header().White; + const isGameLoaded = gameFromUrl !== undefined || !!game.getHeaders().White; if (evaluationProgress) return null;