diff --git a/client/src/App.jsx b/client/src/App.jsx index 9213ab21a..91707006f 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -45208,7 +45208,7 @@ function Home({ onSelect, completedTopics = [], goldMastery = [], coins = 0, isG ))} {featuredApps.map(app => ( - + + + + + )} ) } diff --git a/client/src/BattleApp.jsx b/client/src/BattleApp.jsx index d150798b7..610ae04db 100644 --- a/client/src/BattleApp.jsx +++ b/client/src/BattleApp.jsx @@ -1,1028 +1,237 @@ -import { useState, useEffect, useRef, useCallback, useMemo } from 'react' -import { io } from 'socket.io-client' -import confetti from 'canvas-confetti' - -const API = import.meta.env?.VITE_API_BASE_URL || '' -// socket.io must mount at the same sub-path as the app (e.g. /summership). -// Using just `window.location.origin` (no path) makes the client connect to -// the host's `/socket.io/` endpoint, which on this deployment returns the -// stale static SPA shell instead of the Socket.IO handshake โ†’ "parser error". -// VITE_SOCKET_PATH lets the build-time base drive the path; at runtime we -// fall back to deriving the path from `window.location.pathname` so the -// same build works on root, /summership, or any other sub-path. -const SOCKET_PATH = import.meta.env?.VITE_SOCKET_PATH - || (window.location.pathname.replace(/\/[^/]*$/, '/').replace(/\/+$/, '/') + 'socket.io') -const socket = io(window.location.origin, { path: SOCKET_PATH, transports: ['websocket', 'polling'] }) - -// Inject keyframe styles once -if (typeof document !== 'undefined' && !document.getElementById('battle-animations')) { - const style = document.createElement('style') - style.id = 'battle-animations' - style.textContent = ` - @keyframes timer-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } } - @keyframes timer-urgent { 0%, 100% { background: var(--clr-wrong); box-shadow: 0 0 0 0 rgba(224,90,74,0.6); } 50% { background: #e05a4a; box-shadow: 0 0 0 6px rgba(224,90,74,0); } } - @keyframes timer-warning { 0%, 100% { background: #e8b931; box-shadow: 0 0 0 0 rgba(232,185,49,0.5); } 50% { background: #e8b931; box-shadow: 0 0 0 6px rgba(232,185,49,0); } } - @keyframes shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-6px); } 40% { transform: translateX(6px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(4px); } } - @keyframes reaction-float { 0% { transform: translateY(0) scale(1); opacity: 1; } 100% { transform: translateY(-80px) scale(1.2); opacity: 0; } } - @keyframes score-pop { 0% { transform: scale(1); } 50% { transform: scale(1.25); } 100% { transform: scale(1); } } - @keyframes streak-flame { 0%, 100% { transform: scale(1) rotate(-3deg); } 50% { transform: scale(1.15) rotate(3deg); } } - @keyframes progress-fill { from { width: 0%; } to { width: var(--progress-width); } } - @keyframes answer-reveal { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: translateY(0); } } - ` - document.head.appendChild(style) -} - -const TOPICS = [ - { key: 'arithmetic', label: 'Arithmetic', icon: 'โž•', desc: '+, โˆ’, ร—, รท', color: '#4a90d9' }, - { key: 'multiply', label: 'Tables', icon: 'โœ–๏ธ', desc: 'Times tables', color: '#9b59b6' }, - { key: 'gk', label: 'GK', icon: '๐Ÿง ', desc: 'General Knowledge', color: '#5cb87a' }, - { key: 'sudoku', label: 'Sudoku', icon: '๐Ÿ”ข', desc: 'Solve the grid race', color: '#1abc9c' }, -] - -const ALL_MODULES = [ - { key: 'arithmetic', name: 'Arithmetic', icon: 'โž•', color: '#4a90d9', label: 'Arithmetic' }, - { key: 'multiply', name: 'Tables', icon: 'โœ–๏ธ', color: '#9b59b6', label: 'Tables' }, - { key: 'gk', name: 'GK', icon: '๐Ÿง ', color: '#5cb87a', label: 'GK' }, - { key: 'sudoku', name: 'Sudoku', icon: '๐Ÿ”ข', color: '#1abc9c', label: 'Sudoku' }, - { key: 'addition', name: 'Addition', icon: 'โž•', color: '#4a90d9', label: 'Addition' }, - { key: 'basicarith', name: 'Arithmetic', icon: '๐Ÿ”ข', color: '#7aa2f7', label: 'Arithmetic' }, - { key: 'hcflcm', name: 'HCF & LCM', icon: '๐Ÿ”„', color: '#4a90d9', label: 'HCF & LCM' }, - { key: 'primefactor', name: 'Prime Factors', icon: '๐Ÿ’Ž', color: '#5cb87a', label: 'Prime Factors' }, - { key: 'squaring', name: 'Squaring', icon: '๐Ÿ“', color: '#9b59b6', label: 'Squaring' }, - { key: 'sqrt', name: 'Square Root', icon: 'โˆš', color: '#5cb87a', label: 'Square Root' }, - { key: 'rounding', name: 'Rounding', icon: '๐ŸŽฏ', color: '#4a90d9', label: 'Rounding' }, - { key: 'decimals', name: 'Decimals', icon: 'ยท', color: '#7aa2f7', label: 'Decimals' }, - { key: 'bases', name: 'Number Bases', icon: '๐Ÿ–ฅ', color: '#5cb87a', label: 'Number Bases' }, - { key: 'stdform', name: 'Standard Form', icon: '๐Ÿ”ฌ', color: '#9b59b6', label: 'Standard Form' }, - { key: 'sdt', name: 'Speed, Dist, Time', icon: '๐ŸŽ', color: '#4a90d9', label: 'Speed, Dist, Time' }, - { key: 'fractionadd', name: 'Fractions', icon: 'ยฝ', color: '#5cb87a', label: 'Fractions' }, - { key: 'ratio', name: 'Ratio', icon: 'โš–๏ธ', color: '#4a90d9', label: 'Ratio' }, - { key: 'percent', name: 'Percentages', icon: '%', color: '#7aa2f7', label: 'Percentages' }, - { key: 'profitloss', name: 'Profit & Loss', icon: '๐Ÿ’ฐ', color: '#9b59b6', label: 'Profit & Loss' }, - { key: 'banking', name: 'Banking', icon: '๐Ÿฆ', color: '#4a90d9', label: 'Banking' }, - { key: 'gst', name: 'GST', icon: '๐Ÿงพ', color: '#5cb87a', label: 'GST' }, - { key: 'shares', name: 'Shares', icon: '๐Ÿ“ˆ', color: '#9b59b6', label: 'Shares' }, - { key: 'variation', name: 'Variation', icon: 'โ†”๏ธ', color: '#7aa2f7', label: 'Variation' }, - { key: 'lineareq', name: 'Linear Equations', icon: 'x', color: '#4a90d9', label: 'Linear Equations' }, - { key: 'simul', name: 'Sim. Equations', icon: '{x}', color: '#9b59b6', label: 'Sim. Equations' }, - { key: 'quadratic', name: 'Quadratic', icon: 'xยฒ', color: '#7aa2f7', label: 'Quadratic' }, - { key: 'qformula', name: 'Quadratic Formula', icon: 'ยฑ', color: '#5cb87a', label: 'Quadratic Formula' }, - { key: 'funceval', name: 'Functions', icon: 'f(x)', color: '#4a90d9', label: 'Functions' }, - { key: 'sequences', name: 'Sequences', icon: 'โ€ฆ', color: '#9b59b6', label: 'Sequences' }, - { key: 'indices', name: 'Indices', icon: 'โฟ', color: '#5cb87a', label: 'Indices' }, - { key: 'surds', name: 'Surds', icon: 'โˆšn', color: '#7aa2f7', label: 'Surds' }, - { key: 'log', name: 'Logarithms', icon: 'log', color: '#9b59b6', label: 'Logarithms' }, - { key: 'binomial', name: 'Binomial', icon: 'C(n,k)', color: '#4a90d9', label: 'Binomial' }, - { key: 'complex', name: 'Complex Numbers', icon: 'i', color: '#5cb87a', label: 'Complex Numbers' }, - { key: 'remfactor', name: 'Remainder Thm', icon: 'R(x)', color: '#7aa2f7', label: 'Remainder Thm' }, - { key: 'lineq', name: 'Line Equation', icon: 'mx+c', color: '#4a90d9', label: 'Line Equation' }, - { key: 'ineq', name: 'Inequalities', icon: '<>', color: '#5cb87a', label: 'Inequalities' }, - { key: 'diff', name: 'Differentiation', icon: 'dy/dx', color: '#9b59b6', label: 'Differentiation' }, - { key: 'integ', name: 'Integration', icon: 'โˆซ', color: '#4a90d9', label: 'Integration' }, - { key: 'limits', name: 'Limits', icon: 'lim', color: '#5cb87a', label: 'Limits' }, - { key: 'angles', name: 'Angles', icon: 'โˆ ', color: '#4a90d9', label: 'Angles' }, - { key: 'triangles', name: 'Triangles', icon: 'โ–ณ', color: '#7aa2f7', label: 'Triangles' }, - { key: 'pythag', name: 'Pythagoras', icon: 'โŠฅ', color: '#5cb87a', label: 'Pythagoras' }, - { key: 'polygons', name: 'Polygons', icon: 'โฌก', color: '#9b59b6', label: 'Polygons' }, - { key: 'circleth', name: 'Circle Thms', icon: 'โŠ™', color: '#4a90d9', label: 'Circle Thms' }, - { key: 'coordgeom', name: 'Coord. Geometry', icon: '๐Ÿ“', color: '#7aa2f7', label: 'Coord. Geometry' }, - { key: 'section', name: 'Section Formula', icon: 'รท', color: '#5cb87a', label: 'Section Formula' }, - { key: 'bearings', name: 'Bearings', icon: '๐Ÿงญ', color: '#9b59b6', label: 'Bearings' }, - { key: 'mensur', name: 'Mensuration', icon: '๐Ÿ“', color: '#4a90d9', label: 'Mensuration' }, - { key: 'circmeasure', name: 'Circular Measure', icon: 'ๅผง', color: '#5cb87a', label: 'Circular Measure' }, - { key: 'heron', name: "Heron's Formula", icon: 'โ–ณ', color: '#7aa2f7', label: "Heron's Formula" }, - { key: 'similarity', name: 'Similarity', icon: 'โˆ', color: '#9b59b6', label: 'Similarity' }, - { key: 'congruence', name: 'Congruence', icon: 'โ‰…', color: '#5cb87a', label: 'Congruence' }, - { key: 'transform', name: 'Transformations', icon: 'โ†—', color: '#4a90d9', label: 'Transformations' }, - { key: 'prob', name: 'Probability', icon: '๐ŸŽฒ', color: '#4a90d9', label: 'Probability' }, - { key: 'stats', name: 'Statistics', icon: '๐Ÿ“Š', color: '#7aa2f7', label: 'Statistics' }, - { key: 'permcomb', name: 'Perm & Comb', icon: 'nPr', color: '#9b59b6', label: 'Perm & Comb' }, - { key: 'matrix', name: 'Matrices', icon: 'โ–ฆ', color: '#4a90d9', label: 'Matrices' }, - { key: 'vectors', name: 'Vectors', icon: 'โ†’', color: '#7aa2f7', label: 'Vectors' }, - { key: 'dotprod', name: 'Dot Products', icon: 'ยท', color: '#5cb87a', label: 'Dot Products' }, - { key: 'trig', name: 'Trigonometry', icon: 'โˆก', color: '#5cb87a', label: 'Trigonometry' }, - { key: 'invtrig', name: 'Inverse Trig', icon: 'sinโปยน', color: '#9b59b6', label: 'Inverse Trig' }, - { key: 'vocab', name: 'Vocabulary', icon: '๐Ÿ“–', color: '#4a90d9', label: 'Vocabulary' }, - { key: 'linprog', name: 'Linear Prog.', icon: '๐Ÿ“Š', color: '#5cb87a', label: 'Linear Prog.' }, - { key: 'conics', name: 'Conic Sections', icon: 'โ—ฏ', color: '#9b59b6', label: 'Conic Sections' }, -] - -const MODULE_CATEGORIES = ALL_MODULES.reduce((acc, m) => { - const cat = m.key === 'arithmetic' || m.key === 'multiply' || m.key === 'basicarith' || m.key === 'hcflcm' || m.key === 'primefactor' || m.key === 'squaring' || m.key === 'sqrt' || m.key === 'rounding' || m.key === 'decimals' || m.key === 'bases' || m.key === 'stdform' || m.key === 'sdt' || m.key === 'addition' - ? 'Arithmetic' - : m.key === 'fractionadd' || m.key === 'ratio' || m.key === 'percent' || m.key === 'profitloss' || m.key === 'banking' || m.key === 'gst' || m.key === 'shares' - ? 'Fractions & Ratios' - : m.key === 'variation' || m.key === 'lineareq' || m.key === 'simul' || m.key === 'quadratic' || m.key === 'qformula' || m.key === 'funceval' || m.key === 'sequences' || m.key === 'indices' || m.key === 'surds' || m.key === 'log' || m.key === 'binomial' || m.key === 'complex' || m.key === 'remfactor' || m.key === 'lineq' || m.key === 'ineq' - ? 'Algebra' - : m.key === 'diff' || m.key === 'integ' || m.key === 'limits' - ? 'Calculus' - : m.key === 'angles' || m.key === 'triangles' || m.key === 'pythag' || m.key === 'polygons' || m.key === 'circleth' || m.key === 'coordgeom' || m.key === 'section' || m.key === 'bearings' || m.key === 'mensur' || m.key === 'circmeasure' || m.key === 'heron' || m.key === 'similarity' || m.key === 'congruence' || m.key === 'transform' || m.key === 'conics' - ? 'Geometry' - : m.key === 'prob' || m.key === 'stats' || m.key === 'permcomb' - ? 'Stats & Prob' - : m.key === 'matrix' || m.key === 'vectors' || m.key === 'dotprod' - ? 'Matrices & Vectors' - : 'Trig & Misc' - ;(acc[cat] = acc[cat] || []).push(m) - return acc -}, {}) - -const QUESTION_COUNTS = [3, 5, 10, 15] - -function formatTime(ms) { - if (ms == null) return '-' - const totalSec = Math.round(ms / 1000) - const min = Math.floor(totalSec / 60) - const sec = totalSec % 60 - if (min > 0) return `${min}m ${sec}s` - return `${sec}s` -} - -function loadHistory() { - try { - return JSON.parse(localStorage.getItem('battleHistory') || '[]') - } catch { return [] } -} - -function saveHistory(data) { - try { localStorage.setItem('battleHistory', JSON.stringify(data)) } catch {} -} - -function fireConfetti() { - try { - confetti({ particleCount: 80, spread: 60, origin: { y: 0.7 } }) - setTimeout(() => confetti({ particleCount: 50, spread: 90, origin: { y: 0.6 } }), 200) - } catch {} -} - -function Timer({ timeLeft, totalDuration = 15000 }) { - const pct = Math.max(0, (timeLeft * 1000) / totalDuration) - let color = 'var(--clr-text-soft)' - let timerClass = '' - let barClass = '' - if (timeLeft <= 2) { color = 'var(--clr-wrong)'; timerClass = 'timer-shake' } - else if (timeLeft <= 5) { color = 'var(--clr-wrong)'; timerClass = 'timer-pulse' } - else if (timeLeft <= 10) { color = 'var(--clr-accent)' } - if (timeLeft <= 5) barClass = 'bar-urgent' - else if (timeLeft <= 10) barClass = 'bar-warning' - - return ( - <> - -
- {timeLeft}s -
-
-
-
- - ) -} - -const REACTION_EMOJIS = ['๐Ÿ”ฅ', '๐Ÿ‘', '๐Ÿ˜‚', '๐Ÿ’€', '๐Ÿ‘€', '๐Ÿ˜ฎ'] - -function ReactionBar({ onSend, disabled }) { - const handleClick = (emoji) => { - if (!disabled) { - onSend(emoji) - } - } - return ( -
- {REACTION_EMOJIS.map((e) => ( - - ))} -
- ) -} - -function FloatingReaction({ emoji }) { - return ( -
- {emoji} - -
- ) -} - -export default function BattleApp({ onBack, initialTopic }) { - const [phase, setPhase] = useState('lobby') - const [view, setView] = useState('play') - const [name, setName] = useState('') - const [topic, setTopic] = useState(initialTopic || 'arithmetic') - const [numQuestions, setNumQuestions] = useState(5) - const [roomCode, setRoomCode] = useState('') - const [joinCode, setJoinCode] = useState('') - const [players, setPlayers] = useState([]) - const [myId, setMyId] = useState(socket.id) - const [round, setRound] = useState(0) - const [totalRounds, setTotalRounds] = useState(5) - const [question, setQuestion] = useState(null) - const [answer, setAnswer] = useState('') - const [scores, setScores] = useState([]) - const [roundWinner, setRoundWinner] = useState(null) - const [roundPlayers, setRoundPlayers] = useState([]) - const [opponentAnswered, setOpponentAnswered] = useState(false) - const [answered, setAnswered] = useState(false) - const [answeredTime, setAnsweredTime] = useState(0) - const [timeLeft, setTimeLeft] = useState(0) - const [error, setError] = useState('') - const [moduleSearch, setModuleSearch] = useState('') - const [matchWinner, setMatchWinner] = useState(null) - const [finalScores, setFinalScores] = useState([]) - const [matchHistory, setMatchHistory] = useState([]) - const [matchTopic, setMatchTopic] = useState('') - const [matchNumQ, setMatchNumQ] = useState(5) - const [matchPlayers, setMatchPlayers] = useState([]) - const [history, setHistory] = useState(loadHistory) - const [openRooms, setOpenRooms] = useState({}) - const timerRef = useRef(null) - const answerRef = useRef(null) - const screenShakeRef = useRef(false) - const [streaks, setStreaks] = useState({}) - const [incomingReaction, setIncomingReaction] = useState(null) - const [opponentAnsweredCount, setOpponentAnsweredCount] = useState(0) - const [totalPlayers, setTotalPlayers] = useState(2) - const [sudokuPuzzle, setSudokuPuzzle] = useState(null) - const [sudokuGrid, setSudokuGrid] = useState(null) - const [sudokuRaceActive, setSudokuRaceActive] = useState(false) - const [sudokuOpponentFinished, setSudokuOpponentFinished] = useState(false) - const [sudokuRaceStart, setSudokuRaceStart] = useState(0) - const [sudokuMyTime, setSudokuMyTime] = useState(null) - const [sudokuWrongCount, setSudokuWrongCount] = useState(0) - const [sudokuSelectedCell, setSudokuSelectedCell] = useState(null) - const [isSudokuTopic, setIsSudokuTopic] = useState(false) +import React, { useState, useEffect } from 'react'; +import { io } from 'socket.io-client'; + +export default function BattleApp({ topic, onBack }) { + const [socket, setSocket] = useState(null); + const [status, setStatus] = useState('connecting'); // connecting, waiting, playing, game_over + const [roomId, setRoomId] = useState(null); + const [players, setPlayers] = useState({}); + const [myId, setMyId] = useState(null); + const [question, setQuestion] = useState(null); + const [winner, setWinner] = useState(null); + const [answerInput, setAnswerInput] = useState(''); + const [feedback, setFeedback] = useState(null); // 'correct', 'incorrect' + const [roomCode, setRoomCode] = useState(''); + const [friendCode, setFriendCode] = useState(''); + const [isPrivate, setIsPrivate] = useState(false); useEffect(() => { - socket.on('connect', () => { - setMyId(socket.id) - socket.emit('getOpenRooms') - }) - socket.on('openRooms', (rooms) => setOpenRooms(rooms)) - if (socket.connected) { - setMyId(socket.id) - socket.emit('getOpenRooms') - } - return () => { socket.off('connect'); socket.off('openRooms') } - }, []) - - useEffect(() => { - const resetRoomState = () => { - setPlayers([]) - setQuestion(null) - setAnswer('') - setScores([]) - setRoundWinner(null) - setRoundPlayers([]) - setOpponentAnswered(false) - setOpponentAnsweredCount(0) - setTimeLeft(0) - setRound(0) - setPhase('lobby') - clearInterval(timerRef.current) - } - - socket.on('roomUpdate', ({ players: p, topic: t }) => { - setPlayers(p) - setTotalPlayers(p.length) - if (p.length === 2) { - const readyPlayers = p.filter(player => player.ready) - if (readyPlayers.length === 2 && !players.every((player, idx) => player.ready === p[idx].ready)) { - socket.emit('getOpenRooms') - } - } - }) - - socket.on('matchStart', ({ topic: t, rounds }) => { - setMatchTopic(t) - setTotalRounds(rounds) - setPhase('playing') - setOpponentAnsweredCount(0) - }) - - socket.on('roundStart', ({ prompt, options, type, round: r, total, duration, streaks }) => { - setRound(r) - setTotalRounds(total) - if (streaks) setStreaks(streaks) - setQuestion({ prompt, options, type }) - setAnswer('') - setAnswered(false) - setAnsweredTime(0) - setOpponentAnswered(false) - setOpponentAnsweredCount(0) - setRoundWinner(null) - setRoundPlayers([]) - setPhase('playing') - clearInterval(timerRef.current) - setTimeLeft(Math.ceil(duration / 1000)) - timerRef.current = setInterval(() => { - setTimeLeft(prev => { if (prev <= 1) { clearInterval(timerRef.current); return 0 }; return prev - 1 }) - }, 1000) - setTimeout(() => answerRef.current?.focus(), 100) - }) - - socket.on('opponentAnswered', ({ answeredCount }) => { - setOpponentAnswered(true) - if (answeredCount !== undefined) setOpponentAnsweredCount(answeredCount) - }) - - socket.on('roundResult', ({ winner: w, scores: s, players: rp, streaks: newStreaks }) => { - setRoundWinner(w) - setScores(s) - setRoundPlayers(rp || []) - if (newStreaks) setStreaks(newStreaks) - setPhase('roundResult') - if (w === myId) fireConfetti() - }) - - socket.on('matchEnd', ({ winner: w, finalScores: fs, topic: t, numQuestions: nq, history: h, players: mp, streaks: finalStreaks }) => { - setMatchWinner(w) - setFinalScores(fs) - setMatchTopic(t) - setMatchNumQ(nq) - setMatchHistory(h || []) - setMatchPlayers(mp || []) - if (finalStreaks) setStreaks(finalStreaks) - setPhase('matchEnd') - if (w === myId || w === 'draw') fireConfetti() - const entry = { - id: Date.now(), date: new Date().toISOString(), topic: t, numQuestions: nq, - winner: w, myId: socket.id, myScore: fs.find(s => s.id === socket.id)?.score || 0, - opponentScore: fs.find(s => s.id !== socket.id)?.score || 0, - opponentName: fs.find(s => s.id !== socket.id)?.name || '?', - myName: fs.find(s => s.id === socket.id)?.name || 'You', - history: h || [], - } - const updated = [entry, ...loadHistory()].slice(0, 50) - saveHistory(updated) - setHistory(updated) - }) - - socket.on('opponentLeft', () => { setError('Opponent left the battle.'); resetToLobby() }) - socket.on('openRooms', (rooms) => setOpenRooms(rooms)) - socket.on('error', (msg) => setError(msg)) - - socket.on('sudokuRaceStart', ({ puzzle, raceStart }) => { - setSudokuPuzzle(puzzle) - setSudokuGrid(puzzle.map(row => [...row])) - setSudokuRaceActive(true) - setSudokuRaceStart(raceStart) - setSudokuMyTime(null) - setSudokuWrongCount(0) - setSudokuSelectedCell(null) - setSudokuOpponentFinished(false) - setPhase('sudokuRace') - }) - - socket.on('opponentFinished', ({ playerId, time }) => { - setSudokuOpponentFinished(true) - }) - - socket.on('cellResult', ({ correct, wrongCount }) => { - setSudokuWrongCount(wrongCount || 0) - }) - - socket.on('sudokuForcedEnd', ({ reason }) => { - setSudokuRaceActive(false) - if (reason === 'tooManyWrong') setError('Too many wrong answers! Race ended.') - }) - - return () => { - socket.off('roomUpdate'); socket.off('matchStart'); socket.off('roundStart') - socket.off('roundResult'); socket.off('matchEnd'); socket.off('opponentAnswered') - socket.off('opponentLeft'); socket.off('openRooms'); socket.off('error') - socket.off('sudokuRaceStart'); socket.off('opponentFinished') - socket.off('cellResult'); socket.off('sudokuForcedEnd') - clearInterval(timerRef.current) - } - }, [myId, players]) - - const resetToLobby = useCallback(() => { - setPhase('lobby') - setRoomCode('') - setPlayers([]) - setQuestion(null) - setAnswer('') - setScores([]) - setRoundWinner(null) - setRoundPlayers([]) - setSudokuPuzzle(null) - setSudokuGrid(null) - setSudokuRaceActive(false) - setSudokuWrongCount(0) - setSudokuSelectedCell(null) - setSudokuOpponentFinished(false) - setSudokuMyTime(null) - clearInterval(timerRef.current) - setTimeout(() => socket.emit('getOpenRooms'), 100) - }, []) - - const handleCreate = useCallback(() => { - setError('') - setIsSudokuTopic(topic === 'sudoku') - socket.emit('createRoom', { name: name.trim() || 'Player', topic, numQuestions }, ({ ok, code, players: p, error: e }) => { - if (!ok) { setError(e); return } - setRoomCode(code); setPlayers(p); setMyId(socket.id); setPhase('waiting') - }) - }, [name, topic, numQuestions]) - - const handleJoin = useCallback(() => { - const code = joinCode.trim().toUpperCase() - if (!code) { setError('Enter a room code.'); return } - setError('') - socket.emit('joinRoom', { code, name: name.trim() || 'Player' }, ({ ok, players: p, error: e }) => { - if (!ok) { setError(e); return } - setRoomCode(code); setPlayers(p); setMyId(socket.id); setPhase('waiting') - }) - }, [name, joinCode]) - - const handleReady = useCallback(() => socket.emit('ready'), []) - const handleSubmit = useCallback(() => { - if (!answer.trim() || answered) return - socket.emit('submitAnswer', { answer: answer.trim() }) - setAnswered(true) - setAnsweredTime(15 - timeLeft) - setAnswer('') - }, [answer, answered, timeLeft]) - const handleLeave = useCallback(() => { socket.emit('leave'); resetToLobby() }, [resetToLobby]) - const handleKeyDown = useCallback((e) => { if (e.key === 'Enter' && phase === 'playing' && question) handleSubmit() }, [phase, question, handleSubmit]) - - const handleSudokuCellEdit = useCallback((r, c, val) => { - if (!sudokuPuzzle || sudokuPuzzle[r][c] !== 0) return - const num = Number(val) - if (isNaN(num) || num < 1 || num > 9) return - setSudokuGrid(prev => { - const next = prev.map(row => [...row]) - next[r][c] = num - return next - }) - socket.emit('submitCell', { r, c, val: num }) - }, [sudokuPuzzle]) - - const handleSudokuNumberSelect = useCallback((num) => { - if (!sudokuSelectedCell || !sudokuRaceActive || sudokuWrongCount >= 5) return - const { r, c } = sudokuSelectedCell - if (sudokuPuzzle?.[r]?.[c] !== 0) return - setSudokuGrid(prev => { - const next = prev.map(row => [...row]) - next[r][c] = num - return next - }) - socket.emit('submitCell', { r, c, val: num }) - setSudokuSelectedCell(null) - }, [sudokuSelectedCell, sudokuPuzzle, sudokuRaceActive, sudokuWrongCount]) - - const handleSudokuComplete = useCallback(() => { - setSudokuMyTime(Date.now() - sudokuRaceStart) - socket.emit('sudokuComplete') - }, [sudokuRaceStart]) - - const topicInfo = ALL_MODULES.find(t => t.key === topic) || TOPICS.find(t => t.key === topic) || TOPICS[0] - const myFinalScore = finalScores.find(s => s.id === myId)?.score || 0 - const opponentFinalScore = finalScores.find(s => s.id !== myId)?.score || 0 - const myName = finalScores.find(s => s.id === myId)?.name || 'You' - const opponentName = finalScores.find(s => s.id !== myId)?.name || 'Opponent' - const opponentId = finalScores.find(s => s.id !== myId)?.id - - if (phase === 'lobby' && view === 'history') { + // Connect to backend websocket + const newSocket = io({ path: '/socket.io' }); + setSocket(newSocket); + + newSocket.on('connect', () => { + setMyId(newSocket.id); + setStatus('waiting'); + const code = Math.random().toString(36).substring(2, 6).toUpperCase(); + setRoomCode(code); + newSocket.emit('join_match', { topic }); + }); + + newSocket.on('match_found', (data) => { + setRoomId(data.roomId); + setPlayers({ + [data.p1]: { id: data.p1, score: 0 }, + [data.p2]: { id: data.p2, score: 0 } + }); + setStatus('playing'); + }); + + newSocket.on('new_question', (q) => { + setQuestion(q); + setAnswerInput(''); + setFeedback(null); + }); + + newSocket.on('score_update', (updatedPlayers) => { + setPlayers(updatedPlayers); + setFeedback('correct'); + }); + + newSocket.on('answer_incorrect', () => { + setFeedback('incorrect'); + }); + + newSocket.on('game_over', (data) => { + setPlayers(data.players); + setWinner(data.winner); + setStatus('game_over'); + }); + + newSocket.on('opponent_disconnected', () => { + setStatus('opponent_disconnected'); + }); + + return () => newSocket.disconnect(); + }, [topic]); + + const handleSubmit = (e) => { + e.preventDefault(); + if (!answerInput.trim() || status !== 'playing') return; + + socket.emit('submit_answer', { roomId, answer: answerInput }); + }; + + if (status === 'connecting') { return ( -
-
-
- -

Battle History

-
-
- {history.length === 0 ? ( -

No battles fought yet. Start one!

- ) : ( -
- {history.map(h => { - const t = ALL_MODULES.find(x => x.key === h.topic) || TOPICS.find(x => x.key === h.topic) || TOPICS[0] - const isWin = h.winner === h.myId - const isDraw = h.winner === 'draw' - return ( -
-
-
- {t.icon} - {t.label} - {h.numQuestions}Q -
- - {isDraw ? 'Draw' : isWin ? 'Win' : 'Loss'} - -
-
- {h.myName}: {h.myScore} - vs {h.opponentName}: {h.opponentScore} -
- {h.history && h.history.length > 0 && ( -
-
Questions
- {h.history.map((r, i) => { - const me = r.players?.find(p => p.id === h.myId) - const opp = r.players?.find(p => p.id !== h.myId) - return ( -
- #{i + 1} - {r.prompt} - {r.correctAnswer} - {formatTime(me?.time)} - {formatTime(opp?.time)} -
- ) - })} -
- )} -
{new Date(h.date).toLocaleDateString()} {new Date(h.date).toLocaleTimeString()}
-
- ) - })} -
- )} -
+
+

Connecting to Battle Server...

+
- ) + ); } - if (phase === 'lobby') return ( -
-
-
- -

โš”๏ธ Battle Arena

- -
-

- Race to answer first โ€” fastest finger wins! -

- - {error &&
{error}
} - -
- - setName(e.target.value)} placeholder="Enter your name" maxLength={20} - style={{ width: '100%', padding: '9px 12px', marginTop: 5, borderRadius: 'var(--radius)', border: '1px solid var(--clr-border)', background: 'var(--clr-surface)', color: 'var(--clr-text)', fontSize: '0.95rem', boxSizing: 'border-box' }} /> -
- -
- - setModuleSearch(e.target.value)} placeholder="Search modules..." autoFocus={false} - style={{ width: '100%', padding: '7px 10px', marginTop: 5, borderRadius: 'var(--radius)', border: '1px solid var(--clr-border)', background: 'var(--clr-surface)', color: 'var(--clr-text)', fontSize: '0.85rem', boxSizing: 'border-box' }} /> -
- {Object.entries(MODULE_CATEGORIES).map(([cat, modules]) => { - const filtered = moduleSearch.trim() - ? modules.filter(m => m.name.toLowerCase().includes(moduleSearch.toLowerCase()) || m.key.includes(moduleSearch.toLowerCase())) - : modules - if (!filtered.length) return null - return ( -
-
{cat}
-
- {filtered.map(m => { - const roomCount = (openRooms[m.key] || []).length - return ( - - ) - })} -
-
- ) - })} -
-
- - {topic !== 'sudoku' && ( -
- -
- {QUESTION_COUNTS.map(n => ( - - ))} -
-
- )} - - - - {(openRooms[topic] || []).length > 0 && ( -
-
Open Rooms โ€” {topicInfo.name || topicInfo.label}
-
- {(openRooms[topic] || []).map(r => ( - - ))} -
-
- )} - -
โ€” or join by code โ€”
- -
- setJoinCode(e.target.value.toUpperCase().slice(0, 4))} placeholder="Room code" maxLength={4} - style={{ flex: 1, padding: '9px 12px', borderRadius: 'var(--radius)', border: '1px solid var(--clr-border)', background: 'var(--clr-surface)', color: 'var(--clr-text)', fontSize: '1rem', fontFamily: 'monospace', letterSpacing: '0.2em', textTransform: 'uppercase', boxSizing: 'border-box' }} /> - -
-
-
- ) - - if (phase === 'waiting') return ( -
-
-

Waiting Room

-
- Field: - {topicInfo.icon} {topicInfo.name || topicInfo.label} - ยท - {numQuestions} Questions -
- -
navigator.clipboard?.writeText(roomCode)}> -
{roomCode}
-
click to copy
-
- -
- {players.map((p, i) => ( -
- {p.ready ? 'โœ…' : 'โณ'} - {p.name}{p.id === myId ? ' (you)' : ''} -
- ))} - {players.length < 2 &&
Waiting for opponent...
} -
- -
- - {players.length === 2 && ( - - )} -
-
-
- ) - - if (phase === 'playing') return ( -
-
-
- {topicInfo.icon} {topicInfo.name || topicInfo.label} - {roomCode} - Q{round}/{totalRounds} - -
- -
- {scores.map(s => ( -
+

{isPrivate ? 'Waiting for Friend...' : 'Searching for Opponent...'}

+
Loading...
+ +
+ + +
+ + {!isPrivate && ( +
+

Play with a Friend

+ +
+ setFriendCode(e.target.value.toUpperCase())} + style={{ padding: '0.5rem', width: '120px', textAlign: 'center', borderRadius: '4px', border: '1px solid var(--border)' }} + /> +
- ))} -
- -
-
-
- - {question && ( -
-
{question.prompt}
- {question.hint &&
{question.hint}
}
)} - {question?.grid && question?.row != null && ( -
- {question.grid.map((row, r) => row.map((val, c) => { - const isTarget = r === question.row && c === question.col - const thickR = c % 3 === 2 && c < 8 - const thickB = r % 3 === 2 && r < 8 - return ( -
{val !== 0 ? val : '?'}
- ) - }))} + {isPrivate && ( +
+

Share this code with your friend:

+

{roomCode}

)} - - {question?.type === 'mcq' ? ( -
- {question.options.map((opt, i) => ( - - ))} -
- ) : answered ? ( -
-
โœ“ Answered in {answeredTime}s
-
Waiting for opponent...
-
- ) : ( -
- setAnswer(e.target.value)} onKeyDown={handleKeyDown} - placeholder="Your answer" autoFocus - style={{ flex: 1, padding: '11px 14px', borderRadius: 'var(--radius)', border: '2px solid var(--clr-border)', background: 'var(--clr-surface)', color: 'var(--clr-text)', fontSize: '1.05rem', fontFamily: 'monospace', textAlign: 'center', boxSizing: 'border-box' }} /> - -
- )} - - {opponentAnswered &&
โœ“ Opponent answered ({opponentAnsweredCount}/2)
} - {opponentAnsweredCount === 2 &&
Both players have answered...
}
-
- ) + ); + } - if (phase === 'sudokuRace' && sudokuGrid) { + if (status === 'opponent_disconnected') { return ( -
-
-
- ๐Ÿ”ข Sudoku Race - {roomCode} -
- - {sudokuOpponentFinished && ( -
- Opponent finished! Hurry up! -
- )} +
+

Opponent Disconnected!

+

You win by default!

+ +
+ ); + } - {sudokuWrongCount >= 5 ? ( -
-
Too many wrong answers!
-
You made 5+ mistakes. Race ended.
+ if (status === 'game_over') { + const isWinner = winner === myId; + return ( +
+

Game Over!

+

+ {isWinner ? '๐ŸŽ‰ You Won! ๐ŸŽ‰' : '๐Ÿ’€ You Lost! ๐Ÿ’€'} +

+
+ {Object.values(players).map(p => ( +
+ {p.id === myId ? 'You' : (p.id === 'BOT' ? 'Bot' : 'Opponent')}: {p.score}
- ) : ( - <> -
- Wrong answers: - = 3 ? '#f7768e' : 'var(--clr-text)' }}>{sudokuWrongCount}/5 - {sudokuWrongCount >= 3 && โš  Careful!} -
- -
- {sudokuGrid.map((row, r) => row.map((val, c) => { - const isPuzzle = sudokuPuzzle?.[r]?.[c] !== 0 - const isSelected = sudokuSelectedCell?.r === r && sudokuSelectedCell?.c === c - const thickR = c % 3 === 2 && c < 8 - const thickB = r % 3 === 2 && r < 8 - return ( -
{ - if (!isPuzzle && val === 0 && sudokuRaceActive && sudokuWrongCount < 5) { - setSudokuSelectedCell(isSelected ? null : { r, c }) - } - }} style={{ - display: 'flex', alignItems: 'center', justifyContent: 'center', - aspectRatio: '1', fontSize: 'clamp(0.6rem, 1.8vw, 0.85rem)', fontFamily: 'monospace', - fontWeight: isPuzzle ? 700 : 500, - background: isSelected ? '#1abc9c33' : isPuzzle ? 'var(--clr-surface)' : val !== 0 ? 'rgba(26,188,156,0.12)' : 'rgba(255,255,255,0.5)', - color: isPuzzle ? 'var(--clr-text)' : '#1abc9c', - borderRight: thickR ? '2px solid var(--clr-text)' : '1px solid var(--clr-border)', - borderBottom: thickB ? '2px solid var(--clr-text)' : '1px solid var(--clr-border)', - cursor: !isPuzzle && val === 0 && sudokuRaceActive && sudokuWrongCount < 5 ? 'pointer' : 'default', - }}>{val !== 0 ? val : ''}
- ) - }))} -
- -
- {[1, 2, 3, 4, 5, 6, 7, 8, 9].map(n => ( - - ))} -
- - {sudokuSelectedCell && ( -
- Cell selected โ€” tap a number above to fill -
- )} - -
- - -
- - )} + ))}
+
- ) + ); } - if (phase === 'roundResult') { - const me = roundPlayers.find(p => p.id === myId) - const opp = roundPlayers.find(p => p.id !== myId) - return ( -
-
-

- {roundWinner === myId ? '๐ŸŽ‰ You won the round!' : roundWinner ? 'Opponent got it first' : 'โฑ Time up โ€” draw'} -

- -
-
Question
-
{question?.prompt}
-
Answer: {roundPlayers.length ? roundPlayers[0]?.id === myId ? '' : '' : ''}{(() => { - const h = matchHistory[round - 1] - return h?.correctAnswer || '' - })()}
-
- -
- {roundPlayers.map(p => ( -
-
{p.name}{p.id === myId ? ' (you)' : ''}
-
- {p.correct ? 'โœ“ Correct' : 'โœ— Wrong'} -
-
- {formatTime(p.time)} -
- {streaks[p.id] >= 3 && ( -
๐Ÿ”ฅ{streaks[p.id] >= 5 ? `x${streaks[p.id]}` : ''}
- )} -
- ))} -
+ // Find opponent ID + const opponent = Object.values(players).find(p => p.id !== myId); + const myScore = players[myId] ? players[myId].score : 0; + const oppScore = opponent ? opponent.score : 0; -
- {scores.map(s => ( -
- {s.name}: {s.score} -
- ))} -
-

Next round in 3s...

-
+ return ( +
+
+ +
Live Battle: {topic}
+
- ) - } - if (phase === 'matchEnd') return ( -
-
-
-

- {matchWinner === 'draw' ? "๐Ÿค It's a Draw!" : matchWinner === myId ? '๐Ÿ† You Won!' : 'Better luck next time!'} -

-
- {topicInfo.icon} {topicInfo.name || topicInfo.label} - ยท - {matchNumQ} Questions + {/* Score Trackers */} +
+
+
You
+
{myScore} / 10
+
+
- -
-
-
{myName} (you)
-
{myFinalScore}
- {streaks[myId] >= 3 && ( -
๐Ÿ”ฅ{streaks[myId] >= 5 ? `x${streaks[myId]}` : ''}
- )} -
-
-
{opponentName}
-
{opponentFinalScore}
- {streaks[opponentId] >= 3 && ( -
๐Ÿ”ฅ{streaks[opponentId] >= 5 ? `x${streaks[opponentId]}` : ''}
- )} +
+
{opponent?.id === 'BOT' ? 'Bot' : 'Opponent'}
+
{oppScore} / 10
+
+
+
- {matchHistory.length > 0 && ( -
-
Question Breakdown
-
- {matchHistory.map((r, i) => { - const me = r.players?.find(p => p.id === myId) - const opp = r.players?.find(p => p.id !== myId) - const rowBg = r.winner === myId ? 'rgba(92,184,122,0.06)' : r.winner ? 'rgba(247,118,142,0.04)' : 'transparent' - return ( -
- #{i + 1} -
-
{r.prompt}
-
โ†’ {r.correctAnswer}
- {streaks[me?.id] >= 3 && ( -
๐Ÿ”ฅ{streaks[me?.id] >= 5 ? `x${streaks[me?.id]}` : ''}
- )} -
-
-
{myName}
-
{me?.correct ? 'โœ“' : 'โœ—'} {formatTime(me?.time)}
-
-
-
{opponentName}
-
{opp?.correct ? 'โœ“' : 'โœ—'} {formatTime(opp?.time)}
-
-
- {r.winner === myId ? '๐Ÿ…' : r.winner ? '' : '๐Ÿค'} -
-
- ) - })} -
+ {/* Main Game Area */} +
+ {question && ( +
+

+ +
+ { setAnswerInput(e.target.value); setFeedback(null); }} + style={{ + fontSize: '2rem', + padding: '1rem', + width: '100%', + textAlign: 'center', + borderRadius: '12px', + border: feedback === 'incorrect' ? '3px solid var(--danger)' : '3px solid var(--border)', + outline: 'none', + color: 'var(--text-primary)', + background: 'var(--bg-primary)' + }} + placeholder="Enter answer..." + /> + +
+ + {feedback === 'incorrect' && ( +
Incorrect, try again!
+ )}

)} - -
- - -
- ) - - return null + ); } diff --git a/client/src/utils/audio.js b/client/src/utils/audio.js new file mode 100644 index 000000000..4d9300bda --- /dev/null +++ b/client/src/utils/audio.js @@ -0,0 +1,133 @@ +// utils/audio.js + +// Lazy-initialize audio context to comply with browser autoplay policies +let audioCtx = null; + +function getAudioContext() { + if (!audioCtx) { + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + } + // If the context is suspended (due to autoplay policy), try to resume it + if (audioCtx.state === 'suspended') { + audioCtx.resume(); + } + return audioCtx; +} + +/** + * Play a satisfying high-pitched "ding" for correct answers + */ +export function playCorrectSound() { + try { + const ctx = getAudioContext(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'sine'; + osc.frequency.setValueAtTime(880, ctx.currentTime); // A5 + osc.frequency.exponentialRampToValueAtTime(1760, ctx.currentTime + 0.1); // Slide up to A6 + + gain.gain.setValueAtTime(0, ctx.currentTime); + gain.gain.linearRampToValueAtTime(0.5, ctx.currentTime + 0.05); + gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.5); + + osc.connect(gain); + gain.connect(ctx.destination); + + osc.start(); + osc.stop(ctx.currentTime + 0.5); + } catch (err) { + console.error('Audio playback failed', err); + } +} + +/** + * Play a low buzz for incorrect answers + */ +export function playWrongSound() { + try { + const ctx = getAudioContext(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'sawtooth'; + osc.frequency.setValueAtTime(150, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(100, ctx.currentTime + 0.3); + + gain.gain.setValueAtTime(0, ctx.currentTime); + gain.gain.linearRampToValueAtTime(0.3, ctx.currentTime + 0.05); + gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3); + + osc.connect(gain); + gain.connect(ctx.destination); + + osc.start(); + osc.stop(ctx.currentTime + 0.3); + } catch (err) { + console.error('Audio playback failed', err); + } +} + +/** + * Play an ascending fanfare for winning + */ +export function playWinSound() { + try { + const ctx = getAudioContext(); + const notes = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6 + + notes.forEach((freq, i) => { + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'triangle'; + const startTime = ctx.currentTime + i * 0.15; + + osc.frequency.setValueAtTime(freq, startTime); + + gain.gain.setValueAtTime(0, startTime); + gain.gain.linearRampToValueAtTime(0.4, startTime + 0.05); + gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.8); + + osc.connect(gain); + gain.connect(ctx.destination); + + osc.start(startTime); + osc.stop(startTime + 0.8); + }); + } catch (err) { + console.error('Audio playback failed', err); + } +} + +/** + * Play a descending sad tune for losing + */ +export function playLoseSound() { + try { + const ctx = getAudioContext(); + const notes = [392.00, 370.00, 349.23, 329.63]; // G4, Gb4, F4, E4 + + notes.forEach((freq, i) => { + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + + osc.type = 'square'; + const startTime = ctx.currentTime + i * 0.3; + + osc.frequency.setValueAtTime(freq, startTime); + + gain.gain.setValueAtTime(0, startTime); + gain.gain.linearRampToValueAtTime(0.2, startTime + 0.1); + gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.6); + + osc.connect(gain); + gain.connect(ctx.destination); + + osc.start(startTime); + osc.stop(startTime + 0.6); + }); + } catch (err) { + console.error('Audio playback failed', err); + } +} diff --git a/client/src/utils/confetti.js b/client/src/utils/confetti.js new file mode 100644 index 000000000..f998713aa --- /dev/null +++ b/client/src/utils/confetti.js @@ -0,0 +1,35 @@ +import confetti from 'canvas-confetti'; + +/** + * Triggers a celebratory confetti burst effect. + */ +export function triggerWinConfetti() { + const duration = 3 * 1000; + const animationEnd = Date.now() + duration; + const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 10000 }; + + function randomInRange(min, max) { + return Math.random() * (max - min) + min; + } + + const interval = setInterval(function() { + const timeLeft = animationEnd - Date.now(); + + if (timeLeft <= 0) { + return clearInterval(interval); + } + + const particleCount = 50 * (timeLeft / duration); + // since particles fall down, start a bit higher than random + confetti({ + ...defaults, + particleCount, + origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } + }); + confetti({ + ...defaults, + particleCount, + origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } + }); + }, 250); +} diff --git a/server/index.js b/server/index.js index 620aefbee..f85d795e2 100644 --- a/server/index.js +++ b/server/index.js @@ -49,6 +49,7 @@ const rateLimit = require('express-rate-limit'); const fs = require('fs'); const path = require('path'); const http = require('http'); +const { Server } = require('socket.io'); const wordCreator = require('./wordCreator'); const logger = require('./lib/logger'); diff --git a/server/node_modules/.package-lock.json b/server/node_modules/.package-lock.json deleted file mode 100644 index 3582e7232..000000000 --- a/server/node_modules/.package-lock.json +++ /dev/null @@ -1,1654 +0,0 @@ -{ - "name": "aryabhata-server", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "node_modules/@mongodb-js/saslprep": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.13.tgz", - "integrity": "sha512-E3Sv4eCYAlKYUTx8S3ioQcDUscOif+8zZ5OnW1IzJ+Tt+EO+ke8mn+Y3FX6N1H79picwbdOavVOb1jPi2EOyrg==", - "license": "MIT", - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-proxy": { - "version": "1.17.17", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", - "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", - "license": "MIT" - }, - "node_modules/@types/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", - "license": "MIT", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/bcryptjs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", - "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", - "license": "BSD-3-Clause", - "bin": { - "bcrypt": "bin/bcrypt" - } - }, - "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^2.0.0", - "debug": "^4.4.3", - "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", - "on-finished": "^2.4.1", - "qs": "^6.15.2", - "raw-body": "^3.0.2", - "type-is": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bson": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.1.tgz", - "integrity": "sha512-h/C0qe6857pQhcSJHLfsR1uYGj98Ge3wKAD3Ed9KqH3wcVh+BM4Jq4xISD7vs9OPuT07n+q3QQVjslJ286j6ag==", - "license": "Apache-2.0", - "engines": { - "node": ">=20.19.0" - } - }, - "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/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io": { - "version": "6.6.9", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", - "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "@types/ws": "^8.5.12", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.21.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.1.tgz", - "integrity": "sha512-0D493aP61w0TJ2A0wy27riRsO7FMQ7FK+KUHOKCSfPvYo0R55aiC6emCVgFUeShH0fq0ICPVzNcgoS+BsbXQCA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "ip-address": "^10.2.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.7.tgz", - "integrity": "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.15", - "debug": "^4.3.6", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.3", - "is-plain-object": "^5.0.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": "^14.18.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", - "license": "MIT", - "dependencies": { - "jws": "^4.0.1", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kareem": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.3.0.tgz", - "integrity": "sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "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.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/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "license": "MIT" - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/mongodb": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.5.0.tgz", - "integrity": "sha512-5FnrEDLnvp6ycUOGLNLLU33BfCx2qmp2mJjGPDwKLruYsVzXVSK5fsGpoDXvsXJwBfBsD7ebMRdawbDxC2814g==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/saslprep": "^1.4.11", - "bson": "^7.2.0", - "mongodb-connection-string-url": "^7.0.1" - }, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.806.0", - "@mongodb-js/zstd": "^7.0.0", - "gcp-metadata": "^7.0.1", - "kerberos": "^7.0.0", - "mongodb-client-encryption": "^7.2.0", - "snappy": "^7.3.2", - "socks": "^2.8.6" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.2.tgz", - "integrity": "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==", - "license": "Apache-2.0", - "dependencies": { - "@types/whatwg-url": "^13.0.0", - "whatwg-url": "^14.1.0" - }, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/mongoose": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.8.0.tgz", - "integrity": "sha512-PDGx3XACxrBQyWf4YT+5s1Xsx19x84UWGlRVIza4i9RG6qKjGcoG7odotT6uquR6YoaDMTZ6ZZc/jMXLrNPyyA==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "kareem": "3.3.0", - "mongodb": "~7.5", - "mpath": "0.9.0", - "mquery": "6.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", - "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", - "license": "MIT", - "engines": { - "node": ">=20.19.0" - } - }, - "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/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", - "license": "BSD-3-Clause", - "dependencies": { - "es-define-property": "^1.0.1", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", - "license": "MIT" - }, - "node_modules/socket.io": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", - "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", - "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", - "license": "MIT", - "dependencies": { - "debug": "~4.4.1", - "ws": "~8.21.0" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", - "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "license": "MIT", - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", - "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/server/package.json b/server/package.json index 466ac696c..e0ae006fd 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,8 @@ "http-proxy-middleware": "^3.0.5", "jsonwebtoken": "^9.0.3", "mongoose": "^9.6.1", - "socket.io": "^4.8.3" + "node-fetch": "^3.3.2", + "socket.io": "^4.8.3", + "socket.io-client": "^4.8.3" } }