diff --git a/backend/dist/tsconfig.tsbuildinfo b/backend/dist/tsconfig.tsbuildinfo index 3a193d2..a291666 100644 --- a/backend/dist/tsconfig.tsbuildinfo +++ b/backend/dist/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["../app.ts","../config.ts","../index.ts","../controller/auth/login.ts","../controller/auth/signup.ts","../db/index.ts","../middleware/authenticationJWT.ts","../route/auth.ts","../zod/schema.ts","../zod/types.ts"],"errors":true,"version":"5.8.3"} \ No newline at end of file +{"root":["../app.ts","../config.ts","../index.ts","../controller/auth/login.ts","../controller/auth/signup.ts","../db/index.ts","../middleware/authenticationJWT.ts","../route/auth.ts","../src/generated/prisma/default.d.ts","../src/generated/prisma/edge.d.ts","../src/generated/prisma/index.d.ts","../src/generated/prisma/wasm.d.ts","../src/generated/prisma/runtime/index-browser.d.ts","../src/generated/prisma/runtime/library.d.ts","../zod/schema.ts","../zod/types.ts"],"version":"5.7.3"} \ No newline at end of file diff --git a/frontend/src/component/ChessBoard.tsx b/frontend/src/component/ChessBoard.tsx index 405ef5b..1424b9d 100644 --- a/frontend/src/component/ChessBoard.tsx +++ b/frontend/src/component/ChessBoard.tsx @@ -19,6 +19,7 @@ const ChessBoard = ({ boardFEN, disable, candidates, + squareSize, }: ChessBoardProps) => { const [promotion, setPromotion] = useState<{ file: number; @@ -29,7 +30,13 @@ const ChessBoard = ({ const board = fenToBoard(boardFEN); return ( -
+
{promotion.piece ? ( = ( - _e: React.MouseEvent, + // _e: React.MouseEvent, ) => { actPiece(); }; diff --git a/frontend/src/component/chessboard/Pieces.tsx b/frontend/src/component/chessboard/Pieces.tsx index f6759d5..f61c52a 100644 --- a/frontend/src/component/chessboard/Pieces.tsx +++ b/frontend/src/component/chessboard/Pieces.tsx @@ -91,7 +91,7 @@ const Pieces = ({ turn={turn} piece={piece} square={board[rowI][colI]!.square} - onActive={!disable ? onActive : (_square) => {}} + onActive={!disable ? onActive : () => {}} /> ); } else { diff --git a/frontend/src/features/game/component/GameBoard.tsx b/frontend/src/features/game/component/GameBoard.tsx index 8c84649..5e24e28 100644 --- a/frontend/src/features/game/component/GameBoard.tsx +++ b/frontend/src/features/game/component/GameBoard.tsx @@ -7,7 +7,7 @@ import ChessBoard from "../../../component/ChessBoard"; import { generateCandidates, setActivePiece, -} from "../../../store/features/playGameSlice"; +} from "../../../store/features/gameSlice"; import { ActivePiece, Color } from "../../../types/board"; import { defaultBoardFEN } from "../../../utils/constant"; import { GameBoardProps } from "../../../types/game"; @@ -16,33 +16,35 @@ const GameBoard = ({ onMove, orientation, //TODO: may be we can make this global state (game) turn, + squareSize, }: GameBoardProps) => { - const playGame = useSelector((state: RootState) => state.playGame.value); - const dispach = useDispatch(); + const playGame = useSelector((state: RootState) => state.game.value); //TODO: update this with custom selector + const dispatch = useDispatch(); useEffect(() => { if (playGame?.activePiece) { - dispach( + dispatch( generateCandidates({ square: playGame.activePiece.square, }), ); //TODO: change this to global state (play game state) } - }, [playGame?.activePiece]); + }, [playGame?.activePiece, dispatch]); return ( { //TODO: make type for this - dispach(setActivePiece({ activePiece: active })); //TODO: write this in different place + dispatch(setActivePiece({ activePiece: active as any })); //TODO: write this in different place }} - active={playGame?.activePiece || null} + active={playGame?.activePiece as any || null} orientation={orientation == "w" ? Color.WHITE : Color.BLACK} turn={turn == "w" ? Color.WHITE : Color.BLACK} boardFEN={playGame?.boardFEN || defaultBoardFEN} disable={false} candidates={playGame?.candidates || []} + squareSize={squareSize} /> ); }; diff --git a/frontend/src/features/game/component/GameEnd.tsx b/frontend/src/features/game/component/GameEnd.tsx new file mode 100644 index 0000000..14394d7 --- /dev/null +++ b/frontend/src/features/game/component/GameEnd.tsx @@ -0,0 +1,115 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../store/store"; +import { PlayButton } from "../../../component/ui/playButton"; +import { INIT_GAME } from "../../../utils/messages"; + +interface GameEndProps { + socket: WebSocket | null; +} + +const GameEnd: React.FC = ({ socket }) => { + const game = useSelector((state: RootState) => state.game.value); + + if (!game?.gameEnd || !socket) { + return null; + } + + const getResultText = () => { + switch (game.gameEnd) { + case "white_wins": + return "White Wins!"; + case "black_wins": + return "Black Wins!"; + case "draw": + return "Draw"; + default: + return "Game Over"; + } + }; + + const getResultIcon = () => { + switch (game.gameEnd) { + case "white_wins": + return "👑"; + case "black_wins": + return "👑"; + case "draw": + return "🤝"; + default: + return "🏁"; + } + }; + + const getResultDescription = () => { + const reason = game.gameEndReason; + + if (game.gameEnd === "white_wins" || game.gameEnd === "black_wins") { + if (reason === "checkmate") { + return `Checkmate • ${game.gameEnd === "white_wins" ? "White" : "Black"} is victorious`; + } + return `${game.gameEnd === "white_wins" ? "White" : "Black"} wins`; + } + + if (game.gameEnd === "draw") { + switch (reason) { + case "stalemate": + return "Stalemate • Game drawn"; + case "repetition": + return "Threefold repetition • Game drawn"; + case "insufficient_material": + return "Insufficient material • Game drawn"; + case "50_move_rule": + return "50-move rule • Game drawn"; + default: + return "Game drawn"; + } + } + + return "Game finished"; + }; + + const handleNewGame = () => { + socket.send( + JSON.stringify({ + type: INIT_GAME, + }) + ); + }; + + return ( +
+
+
{getResultIcon()}
+

{getResultText()}

+

{getResultDescription()}

+ +
+ + New Game + + +
+ + {/* Game stats could be added here */} +
+
+ {game.player1.username} + vs + {game.player2.username} +
+
+
+
+ ); +}; + +export default GameEnd; \ No newline at end of file diff --git a/frontend/src/features/game/component/RightSection.tsx b/frontend/src/features/game/component/RightSection.tsx index db2a87b..5626694 100644 --- a/frontend/src/features/game/component/RightSection.tsx +++ b/frontend/src/features/game/component/RightSection.tsx @@ -21,9 +21,7 @@ enum SELECTED { } const RightSection: React.FC = ({ socket }) => { - const moves = useSelector( - (state: RootState) => state.playGame.value?.history, - ); + const moves = useSelector((state: RootState) => state.game.value?.history); const [selected, setSelected] = useState(SELECTED.PLAY); if (!moves) { return ( @@ -49,9 +47,8 @@ const RightSection: React.FC = ({ socket }) => { {/* Header Navigation */}
-
-
+ +
- +
diff --git a/frontend/src/features/game/component/profileTimer.tsx b/frontend/src/features/game/component/profileTimer.tsx index 9f46332..7cdcd23 100644 --- a/frontend/src/features/game/component/profileTimer.tsx +++ b/frontend/src/features/game/component/profileTimer.tsx @@ -1,69 +1,36 @@ //TODO: change color / make color for this -import React, { useState, useEffect } from "react"; +import React from "react"; import { User, Clock } from "lucide-react"; import { Color } from "../../../types/board"; - +import { useTimer } from "../hooks/useTimer"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../store/store"; interface ChessProfileTimerProps { - username?: string; - rating?: number; - initialTime?: number; // in seconds - avatarUrl?: string; - isActive?: boolean; color: Color; - onTimeUp?: () => void; } export const ChessProfileTimer: React.FC = ({ - username = "LShaViR", - rating = 1398, - initialTime = 600, // 10:00 in seconds - avatarUrl, - color = Color.WHITE, - isActive = false, - onTimeUp, + color, }) => { - const [timeLeft, setTimeLeft] = useState(initialTime); - const [isRunning, setIsRunning] = useState(false); - - useEffect(() => { - let interval: NodeJS.Timeout; - - if (isRunning && timeLeft > 0) { - interval = setInterval(() => { - setTimeLeft((prev) => { - if (prev <= 1) { - setIsRunning(false); - onTimeUp?.(); - return 0; - } - return prev - 1; - }); - }, 1000); - } - - return () => clearInterval(interval); - }, [isRunning, timeLeft, onTimeUp]); - + const { timer } = useTimer(color); + const { username, avatarUrl, rating } = useSelector((state: RootState) => + color == Color.WHITE + ? state.game.value?.player1 + : state.game.value?.player2, + ) || { username: "unknown", avatarUrl: "", rating: 800 }; const formatTime = (seconds: number): string => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, "0")}`; }; - const getRatingBars = (rating: number): number => { - if (rating >= 2000) return 4; - if (rating >= 1600) return 3; - if (rating >= 1200) return 2; - return 1; - }; - return (
{/* Left side - Profile */}
{/* Avatar */} -
+
{avatarUrl ? ( = ({ className={color == Color.WHITE ? "text-gray-700" : "text-gray-100"} /> - {formatTime(timeLeft)} + {formatTime(timer)}
diff --git a/frontend/src/features/game/hooks/useMessageHandler.ts b/frontend/src/features/game/hooks/useMessageHandler.ts index 1523fcf..737f522 100644 --- a/frontend/src/features/game/hooks/useMessageHandler.ts +++ b/frontend/src/features/game/hooks/useMessageHandler.ts @@ -1,11 +1,14 @@ import { useDispatch } from "react-redux"; import { messageHandler } from "../utils/messageHandler"; -const useMessageHandler = (socket: WebSocket) => { +const useMessageHandler = (socket: WebSocket | null) => { const dispatch = useDispatch(); - socket.onmessage = async (message) => { - await messageHandler(dispatch, message.data); - }; + + if (socket) { + socket.onmessage = async (message) => { + messageHandler(dispatch, message.data); + }; + } return messageHandler; }; diff --git a/frontend/src/features/game/hooks/useTimer.ts b/frontend/src/features/game/hooks/useTimer.ts new file mode 100644 index 0000000..39282c8 --- /dev/null +++ b/frontend/src/features/game/hooks/useTimer.ts @@ -0,0 +1,53 @@ +import { useSelector } from "react-redux"; +import { RootState } from "../../../store/store"; +import { useEffect, useRef, useState } from "react"; +import { Color } from "../../../types/board"; + +const useTimer = (color: Color) => { + const initialTime = useSelector((state: RootState) => + state.game.value + ? state.game.value[ + color == Color.WHITE ? "player1TimeLeft" : "player2TimeLeft" + ] + : 0, + ); + const gameTurn = useSelector( + (state: RootState) => state.game.value?.gameTurn, + ); + const [timer, setTimer] = useState(0); + const [isRunning, setIsRunning] = useState(false); + const intervalRef = useRef(null); + + useEffect(() => { + setTimer(initialTime); + }, [initialTime]); + + useEffect(() => { + setIsRunning(color == gameTurn ? true : false); + }, [gameTurn, color]); + + useEffect(() => { + if (isRunning && timer > 0) { + intervalRef.current = setInterval(() => { + setTimer((prev) => { + if (prev <= 100) { + setIsRunning(false); + return 0; + } + return prev - 100; + }); + }); + } + if (!isRunning && intervalRef.current) { + clearInterval(intervalRef.current); + } + + return () => { + if (intervalRef.current) clearInterval(intervalRef.current); + }; + }, [isRunning]); + + return { timer }; +}; + +export { useTimer }; diff --git a/frontend/src/features/game/utils/messageHandler.ts b/frontend/src/features/game/utils/messageHandler.ts index cd823ec..bde8f30 100644 --- a/frontend/src/features/game/utils/messageHandler.ts +++ b/frontend/src/features/game/utils/messageHandler.ts @@ -9,13 +9,16 @@ import { OPPONENT_RECONNECTED, } from "../../../utils/messages"; import { Dispatch, UnknownAction } from "@reduxjs/toolkit"; -import { newGame, updateGame } from "../../../store/features/gameSlice"; -import { makeMove, newPlayGame } from "../../../store/features/playGameSlice"; +import { + makeMove, + newGame, + gameOver, +} from "../../../store/features/gameSlice"; import { GameStatus } from "../../../types/game"; export const messageHandler = ( dispatch: Dispatch, - data: string + data: string, ) => { try { const message = JSON.parse(data); @@ -23,7 +26,7 @@ export const messageHandler = ( console.log(message); switch (message.type) { - case INIT_GAME: + case INIT_GAME: { dispatch( newGame({ player1: payload.player1.name, @@ -31,21 +34,19 @@ export const messageHandler = ( turn: payload.turn, gameStatus: GameStatus.RUNNING, gameId: payload.gameId, - }) - ); - dispatch( - newPlayGame({ candidates: [], activePiece: null, - gameEnd: "", boardFEN: new Chess().fen(), history: [], chess: new Chess(), - }) + player1TimeLeft: payload.player1.timeLeft || 600000, + player2TimeLeft: payload.player2.timeLeft || 600000, + gameTurn: payload.turn, + }), ); - break; - case JOIN_AGAIN: + } + case JOIN_AGAIN: { console.log("joingame"); const chess = new Chess(); @@ -59,19 +60,18 @@ export const messageHandler = ( turn: payload.turn, gameStatus: GameStatus.RUNNING, gameId: payload.gameId, - }) - ); - dispatch( - newPlayGame({ candidates: [], activePiece: null, - gameEnd: "", boardFEN: chess.fen(), history: movesHistory, chess: chess, - }) + player1TimeLeft: payload.player1.timeLeft || 600000, + player2TimeLeft: payload.player2.timeLeft || 600000, + gameTurn: payload.turn, + }), ); break; + } case OPPONENT_DISCONNECTED: //TODO: to be implemented break; case OPPONENT_RECONNECTED: //TODO: to be implemented @@ -80,7 +80,7 @@ export const messageHandler = ( dispatch(makeMove({ move: payload.move })); break; case GAME_OVER: - dispatch(updateGame({ gameStatus: payload.result })); + dispatch(gameOver({ gameEnd: payload.result, gameEndReason: payload.reason })); break; default: break; diff --git a/frontend/src/hooks/useSocket.ts b/frontend/src/hooks/useSocket.ts index 45343a8..121877e 100644 --- a/frontend/src/hooks/useSocket.ts +++ b/frontend/src/hooks/useSocket.ts @@ -1,13 +1,20 @@ import { WS_URL } from "../config"; import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; const useSocket = () => { const [socket, setSocket] = useState(null); - - const token = localStorage.getItem("tokenChess"); + const navigate = useNavigate(); useEffect(() => { - if (!token) return; + const token = localStorage.getItem("tokenChess"); + + if (!token) { + console.log("No token found, redirecting to auth"); + navigate("/auth"); + return; + } + const ws = new WebSocket(`${WS_URL}?token=${token}`); ws.onopen = () => { @@ -16,44 +23,22 @@ const useSocket = () => { }; ws.onclose = () => { + console.log("Connection closed"); + setSocket(null); + }; + + ws.onerror = (error) => { + console.error("WebSocket error:", error); setSocket(null); }; return () => { ws.close(); }; - }, [token]); + }, [navigate]); return socket; }; -// import useMessageHandler from "./useMessageHandler"; -// import { useNavigate } from "react-router-dom"; - -// const useSocket = () => { -// const [socket, setSocket] = useState(null); -// const messageHandler = useMessageHandler(); -// const navigate = useNavigate(); - -// useEffect(() => { -// const token = localStorage.getItem("tokenChess"); -// if (!token) { -// navigate("/auth"); -// } -// const newSocket = new WebSocket(`ws://localhost:8080/?token=${token}`); -// newSocket.onopen = () => { -// console.log("Connection established"); -// }; -// newSocket.onmessage = async (message) => { -// console.log("message"); -// // console.log(message); - -// await messageHandler(message.data); -// }; -// setSocket(newSocket); -// return () => newSocket.close(); -// }, []); -// return socket; -// }; export default useSocket; diff --git a/frontend/src/pages/Game.tsx b/frontend/src/pages/Game.tsx index 91fe01f..3a70c92 100644 --- a/frontend/src/pages/Game.tsx +++ b/frontend/src/pages/Game.tsx @@ -6,20 +6,20 @@ import { BottomNavbar, SideNavbar } from "../component/navbar"; import { MOVE } from "../utils/messages"; import useSocket from "../hooks/useSocket"; //TODO: change code for this import RightSection from "../features/game/component/RightSection"; -import GameBoard from "../features/game/component/GameBoard"; import { useSelector } from "react-redux"; import { RootState } from "../store/store"; import { Color, ShortMoveType } from "../types/board"; import useMessageHandler from "../features/game/hooks/useMessageHandler"; import { GameLeftSection } from "../features/game/component/leftGameSection"; +import GameEnd from "../features/game/component/GameEnd"; const Game = () => { const game = useSelector((state: RootState) => state.game.value); const socket = useSocket(); //TODO: make major changes for this - if (socket) { - //TODO: find better thing for that - useMessageHandler(socket); - } else { + + useMessageHandler(socket); + + if (!socket) { return <>; } @@ -45,7 +45,7 @@ const Game = () => { }), ); }} - turn={game?.turn} + turn={game?.turn || Color.WHITE} />
@@ -56,6 +56,7 @@ const Game = () => {
+
); }; diff --git a/frontend/src/store/features/authSlice.ts b/frontend/src/store/features/authSlice.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/store/features/gameSlice.ts b/frontend/src/store/features/gameSlice.ts index f401808..11bd162 100644 --- a/frontend/src/store/features/gameSlice.ts +++ b/frontend/src/store/features/gameSlice.ts @@ -1,16 +1,27 @@ import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; -import { ChessInstance } from "chess.js"; -import { Turn } from "../../types/board"; -import { GameStatus } from "../../types/game"; +import { Color, MoveType, Turn } from "../../types/board"; +import { GameStatus, Player } from "../../types/game"; +import { ChessInstance, Move } from "chess.js"; +import { Square, Piece } from "react-chessboard/dist/chessboard/types"; export interface GameState { value: { - player1: string; - player2: string; + player1: Player; + player2: Player; + player1TimeLeft: number; + player2TimeLeft: number; turn: Turn; gameStatus: GameStatus; gameId: string; + candidates: Move[]; + activePiece: { square: Square; piece: Piece } | null; + boardFEN: string; //TODO: make FEN type using zod + history: MoveType[]; //TODO: Change type + chess: ChessInstance; //TODO: have to change this + gameTurn: Turn; + gameEnd?: "draw" | "white_wins" | "black_wins"; + gameEndReason?: string; } | null; } @@ -25,29 +36,142 @@ export const gameSlice = createSlice({ newGame: ( state, action: PayloadAction<{ - player1: string; - player2: string; + player1: Player; + player2: Player; turn: Turn; gameStatus: GameStatus; gameId: string; - }> + candidates: Move[]; + activePiece: { square: Square; piece: Piece } | null; + boardFEN: string; + history: MoveType[]; + chess: ChessInstance; + player1TimeLeft: number; + player2TimeLeft: number; + gameTurn: Turn; + }>, ) => { + console.log("Inside Init Game"); state.value = action.payload; + console.log(action.payload); }, updateGame: ( state, action: PayloadAction<{ gameStatus: GameStatus; - }> + }>, ) => { if (state.value) { state.value = { ...state.value, gameStatus: action.payload.gameStatus }; } }, + setActivePiece: ( + state, + action: PayloadAction<{ + activePiece: { square: Square; piece: Piece } | null; + }>, + ) => { + if (state.value) { + state.value = { + ...state.value, + activePiece: action.payload.activePiece, + }; + } + }, + clearActivePiece: (state) => { + if (state.value) { + state.value = { + ...state.value, + activePiece: null, + }; + } + }, + generateCandidates: ( + state, + action: PayloadAction<{ + square: Square; + }>, + ) => { + if (state.value) { + const candidates = state.value.chess.moves({ + verbose: true, + square: action.payload.square, + }); + state.value = { ...state.value, candidates: candidates }; + } + }, + clearCandidates: (state) => { + if (state.value) { + state.value = { ...state.value, candidates: [] }; + } + }, + + updateBoard: (state, action: PayloadAction) => { + if (state.value) { + state.value = { ...state.value, boardFEN: action.payload }; + } + }, + addMoveToHistory: (state, action: PayloadAction) => { + if (state.value) { + state.value = { + ...state.value, + history: [...state.value.history, action.payload], + }; + } + }, + gameOver: ( + state, + action: PayloadAction<{ gameEnd: "draw" | "white_wins" | "black_wins"; gameEndReason?: string }>, + ) => { + if (state.value) { + state.value = { + ...state.value, + gameStatus: GameStatus.FINSHED, + gameEnd: action.payload.gameEnd, + gameEndReason: action.payload.gameEndReason, + }; + } + }, + makeMove: (state, action: PayloadAction<{ move: MoveType }>) => { + if (state.value) { + state.value.chess.move(action.payload.move); //TODO: make move local function + const boardFEN = state.value.chess.fen(); + state.value = { + ...state.value, + history: [...state.value.history, action.payload.move], + boardFEN: boardFEN, + candidates: [], + activePiece: null, + player1TimeLeft: + state.value.player1TimeLeft - + (action.payload.move.color == "w" + ? action.payload.move.timeSpent + : 0), + player2TimeLeft: + state.value.player2TimeLeft - + (action.payload.move.color == "b" + ? action.payload.move.timeSpent + : 0), + gameTurn: + state.value.gameTurn == Color.WHITE ? Color.BLACK : Color.WHITE, + }; + } + }, }, }); // Action creators are generated for each case reducer function -export const { newGame, updateGame } = gameSlice.actions; +export const { + newGame, + updateGame, + generateCandidates, + clearCandidates, + setActivePiece, + clearActivePiece, + updateBoard, + addMoveToHistory, + makeMove, + gameOver, +} = gameSlice.actions; export default gameSlice.reducer; diff --git a/frontend/src/store/features/playGameSlice.ts b/frontend/src/store/features/playGameSlice.ts deleted file mode 100644 index 822c298..0000000 --- a/frontend/src/store/features/playGameSlice.ts +++ /dev/null @@ -1,133 +0,0 @@ -//TODO: change name for this slice and file may be board status will we a relevent name and handle some more information in this -import { createSlice } from "@reduxjs/toolkit"; -import type { PayloadAction } from "@reduxjs/toolkit"; -import { Move, Piece, Square } from "../../types/chess"; -import { MoveType } from "../../types/board"; -import { ChessInstance } from "chess.js"; - -export interface PlayGameState { - value: { - candidates: Move[]; - activePiece: { square: Square; piece: Piece } | null; - gameEnd: "w" | "b" | "draw" | ""; - boardFEN: string; //TODO: make FEN type using zod - history: MoveType[]; //TODO: Change type - chess: ChessInstance; //TODO: have to change this - } | null; -} - -const initialState: PlayGameState = { - value: null, -}; - -export const playGameSlice = createSlice({ - name: "playGame", - initialState, - reducers: { - newPlayGame: ( - state, - action: PayloadAction<{ - candidates: Move[]; - activePiece: { square: Square; piece: Piece } | null; - gameEnd: "w" | "b" | "draw" | ""; - boardFEN: string; - history: MoveType[]; - chess: ChessInstance; - }> - ) => { - state.value = action.payload; - }, - setActivePiece: ( - state, - action: PayloadAction<{ - activePiece: { square: Square; piece: Piece } | null; - }> - ) => { - if (state.value) { - state.value = { - ...state.value, - activePiece: action.payload.activePiece, - }; - } - }, - clearActivePiece: (state) => { - if (state.value) { - state.value = { - ...state.value, - activePiece: null, - }; - } - }, - generateCandidates: ( - state, - action: PayloadAction<{ - square: Square; - }> - ) => { - if (state.value) { - const candidates = state.value.chess.moves({ - verbose: true, - square: action.payload.square, - }); - state.value = { ...state.value, candidates: candidates }; - } - }, - clearCandidates: (state) => { - if (state.value) { - state.value = { ...state.value, candidates: [] }; - } - }, - - updateBoard: (state, action: PayloadAction) => { - if (state.value) { - state.value = { ...state.value, boardFEN: action.payload }; - } - }, - addMoveToHistory: (state, action: PayloadAction) => { - if (state.value) { - state.value = { - ...state.value, - history: [...state.value.history, action.payload], - }; - } - }, - gameOver: ( - state, - action: PayloadAction<{ gameEnd: "draw" | "w" | "b" }> - ) => { - if (state.value) { - state.value = { - ...state.value, - gameEnd: action.payload.gameEnd, - }; - } - }, - makeMove: (state, action: PayloadAction<{ move: MoveType }>) => { - if (state.value) { - state.value.chess.move(action.payload.move); //TODO: make move local function - const boardFEN = state.value.chess.fen(); - state.value = { - ...state.value, - history: [...state.value.history, action.payload.move], - boardFEN: boardFEN, - candidates: [], - activePiece: null, - }; - } - }, - }, -}); - -// Action creators are generated for each case reducer function -export const { - newPlayGame, - generateCandidates, - clearCandidates, - setActivePiece, - clearActivePiece, - updateBoard, - addMoveToHistory, - makeMove, -} = playGameSlice.actions; - -export default playGameSlice.reducer; diff --git a/frontend/src/store/features/uiSlice.ts b/frontend/src/store/features/uiSlice.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/store/features/userSlice.ts b/frontend/src/store/features/userSlice.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index e70f052..c09ec29 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -1,11 +1,9 @@ import { configureStore } from "@reduxjs/toolkit"; import gameReducer from "./features/gameSlice"; -import playGameReducer from "./features/playGameSlice"; export const store = configureStore({ reducer: { game: gameReducer, - playGame: playGameReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ diff --git a/frontend/src/types/board.ts b/frontend/src/types/board.ts index a2528b0..6e14d41 100644 --- a/frontend/src/types/board.ts +++ b/frontend/src/types/board.ts @@ -17,6 +17,7 @@ export type ChessBoardProps = { boardFEN: string; //TODO: check if it is valid fen or not disable: boolean; candidates: Move[]; //TODO: change move type + squareSize: number; }; export type PromotionProps = { diff --git a/frontend/src/types/game.ts b/frontend/src/types/game.ts index da28e99..5f00729 100644 --- a/frontend/src/types/game.ts +++ b/frontend/src/types/game.ts @@ -4,11 +4,17 @@ export type GameBoardProps = { onMove: (move: ShortMoveType) => void; orientation: Orientation; turn: Turn; + squareSize: number; }; export enum GameStatus { RUNNING = "running", - WHITE_WINS = "w", - BLACK_WINS = "b", - DRAW = "draw", + FINSHED = "finished", + INCOMPLETE = "incomplete", } + +export type Player = { + username: string; + avatarUrl: string; + rating: number; +}; diff --git a/frontend/src/utils/chess/fenToBoard.ts b/frontend/src/utils/chess/fenToBoard.ts index dee8c9f..dbac6fc 100644 --- a/frontend/src/utils/chess/fenToBoard.ts +++ b/frontend/src/utils/chess/fenToBoard.ts @@ -16,7 +16,7 @@ export function fenToBoard(fen: string): BoardType { if (!isNaN(Number(char))) { const emptySquares = Number(char); for (let i = 0; i < emptySquares; i++) { - const square = (files[fileIndex] + (8 - rowIndex)) as Square; + // const square = (files[fileIndex] + (8 - rowIndex)) as Square; row.push(null); fileIndex++; } diff --git a/frontend/src/utils/chessboard/helper.ts b/frontend/src/utils/chessboard/helper.ts index c707844..c6f1493 100644 --- a/frontend/src/utils/chessboard/helper.ts +++ b/frontend/src/utils/chessboard/helper.ts @@ -39,7 +39,7 @@ export const isValidMove = ( }; export const isPromotion = ({ - from, + // from, to, piece, }: { diff --git a/ws/package-lock.json b/ws/package-lock.json index eb8d778..721a3af 100644 --- a/ws/package-lock.json +++ b/ws/package-lock.json @@ -20,7 +20,8 @@ "zod": "^3.24.2" }, "devDependencies": { - "prisma": "^6.8.2" + "prisma": "^6.8.2", + "ts-to-zod": "^3.15.0" } }, "node_modules/@babel/runtime": { @@ -35,6 +36,74 @@ "node": ">=6.9.0" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oclif/core": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.3.0.tgz", + "integrity": "sha512-lIzHY+JMP6evrS5E/sGijNnwrCoNtGy8703jWXcMuPOYKiFhWoAqnIm1BGgoRgmxczkbSfRsHUL/lwsSgh74Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.0", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^9.0.5", + "semver": "^7.6.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@prisma/config": { "version": "6.8.2", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.8.2.tgz", @@ -153,18 +222,386 @@ "@types/node": "*" } }, + "node_modules/@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "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/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/case": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, "node_modules/chess.js": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/chess.js/-/chess.js-0.13.4.tgz", "integrity": "sha512-li8pyEtvfw6V7xvVr5aUh8OuICfviWNT3YXPh56/wNZ8yEdqdAaWcWargK6xTmqsWKKH5c1bvdJ7lh5vqNv2wg==", "license": "BSD-2-Clause" }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/crypto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", @@ -172,6 +609,50 @@ "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", "license": "ISC" }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", @@ -192,12 +673,254 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/fast-deep-equal": { + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -207,6 +930,273 @@ "react-is": "^16.7.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-observable": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", + "integrity": "sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -217,6 +1207,19 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -260,6 +1263,26 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -284,29 +1307,209 @@ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/observable-fns": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", + "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, "node_modules/prisma": { "version": "6.8.2", @@ -334,6 +1537,27 @@ } } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -427,6 +1651,34 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -442,6 +1694,75 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -462,6 +1783,13 @@ ], "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", @@ -481,12 +1809,296 @@ "node": ">=10" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "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/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/threads": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", + "integrity": "sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0", + "debug": "^4.2.0", + "is-observable": "^2.1.0", + "observable-fns": "^0.6.1" + }, + "funding": { + "url": "https://github.com/andywer/threads.js?sponsor=1" + }, + "optionalDependencies": { + "tiny-worker": ">= 2" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-worker": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", + "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "esm": "^3.2.25" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-to-zod": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/ts-to-zod/-/ts-to-zod-3.15.0.tgz", + "integrity": "sha512-Lu5ITqD8xCIo4JZp4Cg3iSK3J2x3TGwwuDtNHfAIlx1mXWKClRdzqV+x6CFEzhKtJlZzhyvJIqg7DzrWfsdVSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": ">=3.26.0", + "@typescript/vfs": "^1.5.0", + "case": "^1.6.3", + "chokidar": "^3.5.1", + "fs-extra": "^11.1.1", + "inquirer": "^8.2.0", + "lodash": "^4.17.21", + "ora": "^5.4.0", + "prettier": "3.0.3", + "rxjs": "^7.4.0", + "slash": "^3.0.0", + "threads": "^1.7.0", + "tslib": "^2.3.1", + "tsutils": "^3.21.0", + "typescript": "^5.2.2", + "zod": "^3.23.8" + }, + "bin": { + "ts-to-zod": "bin/run" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", diff --git a/ws/package.json b/ws/package.json index d9e897d..04b883a 100644 --- a/ws/package.json +++ b/ws/package.json @@ -25,6 +25,7 @@ "zod": "^3.24.2" }, "devDependencies": { - "prisma": "^6.8.2" + "prisma": "^6.8.2", + "ts-to-zod": "^3.15.0" } } diff --git a/ws/prisma/schema.prisma b/ws/prisma/schema.prisma index 5fe9b27..9dc315c 100644 --- a/ws/prisma/schema.prisma +++ b/ws/prisma/schema.prisma @@ -38,6 +38,7 @@ model User { username String @unique password String name String + avatarURL String? rating Int @default(800) gamesAsWhite Game[] @relation("gamesAsWhite") gamesAsBlack Game[] @relation("gamesAsBlack") diff --git a/ws/src/Game.ts b/ws/src/Game.ts index 01d99d0..fd07e08 100644 --- a/ws/src/Game.ts +++ b/ws/src/Game.ts @@ -31,10 +31,47 @@ export class Game { : GameStatus.black; //TODO: update DB } + + getGameEndReason(): string { + if (this.chess.in_checkmate()) { + return "checkmate"; + } else if (this.chess.in_stalemate()) { + return "stalemate"; + } else if (this.chess.in_threefold_repetition()) { + return "repetition"; + } else if (this.chess.insufficient_material()) { + return "insufficient_material"; + } else if (this.chess.in_draw()) { + return "50_move_rule"; + } + return "unknown"; + } + + checkGameEnd(): { isGameOver: boolean; result?: "white_wins" | "black_wins" | "draw"; reason?: string } { + if (this.chess.game_over()) { + const reason = this.getGameEndReason(); + let result: "white_wins" | "black_wins" | "draw"; + + if (this.chess.in_checkmate()) { + result = this.chess.turn() === "w" ? "black_wins" : "white_wins"; + } else { + result = "draw"; + } + + return { isGameOver: true, result, reason }; + } + return { isGameOver: false }; + } move(move: ShortMove) { const detailedMove: Move | null = this.chess.move(move); if (detailedMove) { - const newMove: MoveType = { ...detailedMove, timeSpent: 0 }; + const newMove: MoveType = { + from: detailedMove.from, + to: detailedMove.to, + promotion: detailedMove.promotion || "q", + san: detailedMove.san, + timeSpent: 0 + }; this.history.push(newMove); } else { throw { message: "invalid move" }; diff --git a/ws/src/GameManager.ts b/ws/src/GameManager.ts index 1cdf705..30d92a1 100644 --- a/ws/src/GameManager.ts +++ b/ws/src/GameManager.ts @@ -10,7 +10,6 @@ import { OPPONENT_RECONNECTED, } from "./messages"; import { User } from "./User"; -import { WebSocket } from "ws"; import { messageSchema } from "./zod/schema"; import { brodcastMessage } from "./utils/brodcast"; import { BLACK_WINS, DRAW, WHITE_WINS } from "./utils/constant"; @@ -105,7 +104,7 @@ export class GameManager { turn: user.id == player1.id ? "w" : "b", pgn: game.chess.pgn(), }, - }) + }), ); waitingPlayer.socket.send( JSON.stringify({ @@ -118,7 +117,7 @@ export class GameManager { player2: { id: player2.id, name: player2.name }, turn: user.id == player1.id ? "w" : "b", }, - }) + }), ); return; } @@ -163,7 +162,7 @@ export class GameManager { }, turn: this.pendingUserId == game.player1 ? "w" : "b", }, - }) + }), ); user.socket.send( JSON.stringify({ @@ -179,7 +178,7 @@ export class GameManager { }, turn: userId == game.player1 ? "w" : "b", }, - }) + }), ); this.pendingUserId = ""; } else { @@ -190,7 +189,7 @@ export class GameManager { payload: { message: "pending use set", }, - }) + }), ); } break; @@ -222,15 +221,12 @@ export class GameManager { move, turn: game.chess.turn() == "w" ? "b" : "w", }, - }) + }), ); //TODO: check for if socket is there or not - if (game.chess.game_over()) { - //TODO: make different message for different types of draw and wins - const result = game.chess.in_checkmate() - ? game.chess.turn() == "b" - ? WHITE_WINS - : BLACK_WINS - : DRAW; + + const gameEndCheck = game.checkGameEnd(); + if (gameEndCheck.isGameOver) { + game.gameOver(gameEndCheck.result!); brodcastMessage( [ this.users.get(game.player1)?.user?.socket, @@ -239,9 +235,10 @@ export class GameManager { JSON.stringify({ type: GAME_OVER, payload: { - result: result, + result: gameEndCheck.result, + reason: gameEndCheck.reason, }, - }) + }), ); } } @@ -271,7 +268,7 @@ export class GameManager { otherUser.user.socket.send( JSON.stringify({ type: OPPONENT_DISCONNECTED, - }) + }), ); } } diff --git a/ws/src/GameManager2.ts b/ws/src/GameManager2.ts new file mode 100644 index 0000000..9fc4f82 --- /dev/null +++ b/ws/src/GameManager2.ts @@ -0,0 +1,60 @@ +import { Game } from "./Game"; +import { + IncomingMessage, + incomingMessageSchema, + initGamePayloadSchema, + SupportedIncomingMessage, +} from "./messages/incomingMessages"; +import { User } from "./User"; + +export class GameManager2 { + private games: Map; + private users: Map; + private pendingUser: string | null; + + private constructor() { + this.games = new Map(); + this.users = new Map(); + this.pendingUser = null; + } + + addUser(user: User) { + this.users.set(user.id, user); + this.requestHandler(user.id); + } + + requestHandler(userId: string) { + try { + const user = this.users.get(userId); + if (!user) { + return; + } + const socket = user.socket; + + if (!socket) { + return; + } + + socket.on("message", (message: IncomingMessage) => { + try { + const messageResult = incomingMessageSchema.safeParse(message); + if (!messageResult.success) { + throw new Error("message is of invalid type"); + } + const { type, payload } = messageResult.data; + switch (type) { + case SupportedIncomingMessage.INIT_GAME: + if (this.pendingUser) { + } else { + this.pendingUser = userId; + } + } + } catch (error) { + console.error(error); + } + }); + } catch (error) { + console.error(error); + } + } +} diff --git a/ws/src/User.ts b/ws/src/User.ts index 61522e4..f6f7eb7 100644 --- a/ws/src/User.ts +++ b/ws/src/User.ts @@ -4,13 +4,13 @@ import { WebSocket } from "ws"; export class User { public socket: WebSocket; public id: string; - public name: string; public gameId: string; + public name: string; constructor(socket: WebSocket, userJwtClaims?: userJwtClaims) { this.socket = socket; this.id = userJwtClaims?.id || randomUUID(); - this.name = userJwtClaims?.name || "lucky"; + this.name = userJwtClaims?.username || `User${Math.floor(Math.random() * 1000)}`; this.gameId = ""; } diff --git a/ws/src/auth/index.ts b/ws/src/auth/index.ts index 7782f71..02054d2 100644 --- a/ws/src/auth/index.ts +++ b/ws/src/auth/index.ts @@ -6,12 +6,12 @@ const JWT_SECRET = process.env.JWT_SECRET || "secretKey"; export interface userJwtClaims { id: string; - name: string; + username?: string; } //TODO: update this function export const extractAuthUser = ( token: string, - socket: WebSocket + socket: WebSocket, ): User | undefined => { console.log(JWT_SECRET); try { diff --git a/ws/src/messages/incomingMessages.ts b/ws/src/messages/incomingMessages.ts new file mode 100644 index 0000000..514e206 --- /dev/null +++ b/ws/src/messages/incomingMessages.ts @@ -0,0 +1,49 @@ +import z from "zod"; +import { shortMoveSchema } from "../types"; + +export enum SupportedIncomingMessage { + INIT_GAME = "init_game", + MOVE = "move", + JOIN_ROOM = "join_room", + EXIT_GAME = "exit_game", +} + +export const initGamePayloadSchema = z.object({ + playerId: z.string(), +}); + +export const joinRoomPayloadSchema = z.object({ + playerId: z.string(), + gameId: z.string(), +}); + +export const exitGamePayloadSchema = z.object({ + playerId: z.string(), + gameId: z.string(), +}); + +export const incomingMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(SupportedIncomingMessage.INIT_GAME), + payload: initGamePayloadSchema, + }), + z.object({ + type: z.literal(SupportedIncomingMessage.MOVE), + payload: shortMoveSchema, + }), + z.object({ + type: z.literal(SupportedIncomingMessage.JOIN_ROOM), + payload: joinRoomPayloadSchema, + }), + z.object({ + type: z.literal(SupportedIncomingMessage.EXIT_GAME), + payload: exitGamePayloadSchema, + }), +]); + +export type InitGamePayloadType = z.infer; +export type MoveInPayloadType = z.infer; +export type JoinRoomPayloadType = z.infer; +export type ExitGamePayloadType = z.infer; + +export type IncomingMessage = z.infer; diff --git a/ws/src/messages/outgoingMessages.ts b/ws/src/messages/outgoingMessages.ts new file mode 100644 index 0000000..37c2f6f --- /dev/null +++ b/ws/src/messages/outgoingMessages.ts @@ -0,0 +1,81 @@ +import z from "zod"; +import { gameStatusSchema, moveSchema, playerGameSchema } from "../types"; + +export enum SupportedOutgoingMessage { + GAME_ADDED = "game_added", + GAME_STARTED = "game_started", + MOVE = "move", + OPPONENT_DISCONNECTED = "opponent_disconnected", + OPPONENT_RECONNECTED = "opponent_reconnected", + GAME_NOT_FOUND = "game_not_found", + GAME_JOINED_AGAIN = "game_joined_again", + GAME_OVER = "game_over", +} + +export const gameAddedPayloadSchema = z.object({ + gameId: z.string(), +}); + +export const gameStartedPayloadSchema = z.object({ + player1: playerGameSchema, + player2: playerGameSchema, + timeControl: z.enum(["classical", "rapid", "blitz"]), + gameTime: z.number(), + gameId: z.string(), +}); + +export const gameJoinAgainPayloadSchema = z.object({ + player1: playerGameSchema, + player2: playerGameSchema, + timeControl: z.enum(["classical", "rapic", "blitz"]), + player1TimeLeft: z.number(), + player2TimeLeft: z.number(), + gameId: z.string(), + history: z.array(moveSchema), + gameTurn: z.enum(["w", "b"]), + gameStatus: gameStatusSchema, +}); + +export const gameOverPayloadSchema = z.object({ + result: z.enum(["black_wins", "white_wins", "draw"]), + reason: z.string().optional(), +}); + +export type GameAddedPayloadType = z.infer; +export type GameStartedPayloadType = z.infer; +export type MoveOutPayloadType = z.infer; +export type GameJoinAgainPayloadType = z.infer< + typeof gameJoinAgainPayloadSchema +>; +export type GameOverPayloadType = z.infer; + +export type OutgoingMessages = + | { + type: SupportedOutgoingMessage.GAME_ADDED; + payload: GameAddedPayloadType; + } + | { + type: SupportedOutgoingMessage.GAME_STARTED; + payload: GameStartedPayloadType; + } + | { + type: SupportedOutgoingMessage.MOVE; + payload: MoveOutPayloadType; + } + | { + type: SupportedOutgoingMessage.OPPONENT_DISCONNECTED; + } + | { + type: SupportedOutgoingMessage.OPPONENT_RECONNECTED; + } + | { + type: SupportedOutgoingMessage.GAME_NOT_FOUND; + } + | { + type: SupportedOutgoingMessage.GAME_JOINED_AGAIN; + payload: GameJoinAgainPayloadType; + } + | { + type: SupportedOutgoingMessage.GAME_OVER; + payload: GameOverPayloadType; + }; diff --git a/ws/src/types.ts b/ws/src/types.ts index eefd78a..427232f 100644 --- a/ws/src/types.ts +++ b/ws/src/types.ts @@ -1,13 +1,41 @@ -import { Move } from "chess.js"; +import { z } from "zod"; -export enum GameStatus { +export const gameStatusSchema = z.enum([ "running", - "white", - "black", + "white_wins", + "black_wins", "draw", "unfinished", -} +]); + +export const shortMoveSchema = z.object({ + from: z.string().regex(/^[a-h][1-8]$/), + to: z.string().regex(/^[a-h][1-8]$/), + promotion: z.enum(["q", "r", "b", "n"]), +}); + +export const moveSchema = z.object({ + from: z.string().regex(/^[a-h][1-8]$/), + to: z.string().regex(/^[a-h][1-8]$/), + promotion: z.enum(["q", "r", "b", "n"]), + san: z.string(), + timeSpent: z.number(), +}); -export interface MoveType extends Move { - timeSpent: number; +export const playerGameSchema = z.object({ + username: z.string(), + rating: z.string(), + avatarUrl: z.string(), +}); + +export enum GameStatus { + running = "running", + white = "white_wins", + black = "black_wins", + draw = "draw", + unfinished = "unfinished", } + +export type ShortMoveType = z.infer; +export type MoveType = z.infer; +export type GameStatusType = z.infer; diff --git a/ws/tsconfig.tsbuildinfo b/ws/tsconfig.tsbuildinfo index 630da64..ec71446 100644 --- a/ws/tsconfig.tsbuildinfo +++ b/ws/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/Game.ts","./src/GameManager.ts","./src/User.ts","./src/index.ts","./src/messages.ts","./src/types.ts","./src/auth/index.ts","./src/utils/brodcast.ts","./src/utils/constant.ts","./src/utils/initGame.ts","./src/zod/schema.ts"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/Game.ts","./src/GameManager.ts","./src/GameManager2.ts","./src/User.ts","./src/index.ts","./src/messages.ts","./src/types.ts","./src/auth/index.ts","./src/messages/incomingMessages.ts","./src/messages/outgoingMessages.ts","./src/utils/brodcast.ts","./src/utils/constant.ts","./src/utils/initGame.ts","./src/zod/schema.ts"],"errors":true,"version":"5.8.3"} \ No newline at end of file