- Réalisé avec ❤ à Montréal par des finissant•e•s de l'ETS
+ Réalisé avec ❤ à Montréal par des finissant•e•s de l'ETS
diff --git a/client/src/components/GIFTCheatSheet/GiftCheatSheet.tsx b/client/src/components/GIFTCheatSheet/GiftCheatSheet.tsx
index 6e13395..671ae9d 100644
--- a/client/src/components/GIFTCheatSheet/GiftCheatSheet.tsx
+++ b/client/src/components/GIFTCheatSheet/GiftCheatSheet.tsx
@@ -28,9 +28,9 @@ const GiftCheatSheet: React.FC = () => {
const QuestionNum ="Question {#=Nombre\n} //OU \nQuestion {#=Nombre:Tolérance\n} // OU \nQuestion {#=PetitNombre..GrandNombre\n}\n// La tolérance est un pourcentage.\n// La réponse doit être comprise entre PetitNombre et GrandNombre";
return (
-
Informations pratiques sur l'éditeur
+
Informations pratiques sur l'éditeur
- L'éditeur utilise le format GIFT (General Import Format Template) créé pour la
+ L'éditeur utilise le format GIFT (General Import Format Template) créé pour la
plateforme Moodle afin de générer les mini-tests. Ci-dessous vous pouvez retrouver la
syntaxe pour chaque type de question :
@@ -126,7 +126,7 @@ const GiftCheatSheet: React.FC = () => {
7. Paramètres optionnels
Si vous souhaitez utiliser certains caractères spéciaux dans vos énoncés,
- réponses ou feedback, vous devez 'échapper' ces derniers en ajoutant un \
+ réponses ou feedback, vous devez «échapper» ces derniers en ajoutant un \
devant:
@@ -140,9 +140,9 @@ const GiftCheatSheet: React.FC = () => {
8. LaTeX et Markdown
Les formats LaTeX et Markdown sont supportés dans cette application. Vous devez cependant penser
- à 'échapper' les caractères spéciaux mentionnés plus haut.
+ à «échapper» les caractères spéciaux mentionnés plus haut.
- Exemple d'équation:
+ Exemple d'équation:
{'$$x\\= \\frac\\{y^2\\}\\{4\\}$$'}
{'\n$x\\= \\frac\\{y^2\\}\\{4\\}$'}
@@ -167,16 +167,16 @@ const GiftCheatSheet: React.FC = () => {
{'")'}
- Exemple d'une question Vrai/Faux avec l'image d'un chat:
+ Exemple d'une question Vrai/Faux avec l'image d'un chat:
{'[markdown]Ceci est un chat: \n\n{T}'}
- Note : les images étant spécifiées avec la syntaxe Markdown dans GIFT, on doit échapper les caractères spéciales (:) dans l'URL de l'image.
+ Note : les images étant spécifiées avec la syntaxe Markdown dans GIFT, on doit échapper les caractères spéciales (:) dans l'URL de l'image.
Note : On ne peut utiliser les images dans les messages de rétroaction (GIFT), car les rétroactions ne supportent pas le texte avec formatage (Markdown).
- Attention: l'ancienne fonctionnalité avec les balises {' '} n'est plus
+ Attention: l'ancienne fonctionnalité avec les balises {' '} n'est plus
supportée.
@@ -184,7 +184,7 @@ const GiftCheatSheet: React.FC = () => {
10. Informations supplémentaires
- GIFT supporte d'autres formats de questions que nous ne gérons pas sur cette
+ GIFT supporte d'autres formats de questions que nous ne gérons pas sur cette
application.
Vous pouvez retrouver la Documentation de GIFT (en anglais):
diff --git a/client/src/components/GiftTemplate/templates/TextType.ts b/client/src/components/GiftTemplate/templates/TextType.ts
index c6d8ecf..02a7a14 100644
--- a/client/src/components/GiftTemplate/templates/TextType.ts
+++ b/client/src/components/GiftTemplate/templates/TextType.ts
@@ -30,7 +30,7 @@ export function formatLatex(text: string): string {
*/
export default function textType({ text }: TextTypeOptions) {
const formatText = formatLatex(text.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
-
+ let parsedText = '';
switch (text.format) {
case 'moodle':
case 'plain':
@@ -40,7 +40,7 @@ export default function textType({ text }: TextTypeOptions) {
// Strip outer paragraph tags (not a great approach with regex)
return formatText.replace(/(^
)(.*?)(<\/p>)$/gm, '$2');
case 'markdown':
- const parsedText = marked.parse(formatText, { breaks: true }) as string; // https://github.com/markedjs/marked/discussions/3219
+ parsedText = marked.parse(formatText, { breaks: true }) as string; // https://github.com/markedjs/marked/discussions/3219
return parsedText.replace(/(^
)(.*?)(<\/p>)$/gm, '$2');
default:
throw new Error(`Unsupported text format: ${text.format}`);
diff --git a/client/src/components/ImportModal/ImportModal.tsx b/client/src/components/ImportModal/ImportModal.tsx
index a00b8bb..41ab6ca 100644
--- a/client/src/components/ImportModal/ImportModal.tsx
+++ b/client/src/components/ImportModal/ImportModal.tsx
@@ -168,7 +168,7 @@ const DragAndDrop: React.FC = ({ handleOnClose, handleOnImport, open, sel
Déposer des fichiers ici ou
- cliquez pour ouvrir l'explorateur des fichiers
+ cliquez pour ouvrir l'explorateur des fichiers
diff --git a/client/src/components/LaunchQuizDialog/LaunchQuizDialog.tsx b/client/src/components/LaunchQuizDialog/LaunchQuizDialog.tsx
index e174b23..3bd307b 100644
--- a/client/src/components/LaunchQuizDialog/LaunchQuizDialog.tsx
+++ b/client/src/components/LaunchQuizDialog/LaunchQuizDialog.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import {
Button,
Dialog,
diff --git a/client/src/components/LiveResults/LiveResults.tsx b/client/src/components/LiveResults/LiveResults.tsx
index f08ad27..b7d1c72 100644
--- a/client/src/components/LiveResults/LiveResults.tsx
+++ b/client/src/components/LiveResults/LiveResults.tsx
@@ -300,7 +300,7 @@ const LiveResults: React.FC
= ({ questions, showSelectedQuesti
- Nom d'utilisateur
+ Nom d'utilisateur
{Array.from({ length: maxQuestions }, (_, index) => (
= ({
questionTypeComponent = (
({ ...choice, id: index.toString() }))}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
globalFeedback={question.globalFeedback?.text}
@@ -78,7 +78,7 @@ const Question: React.FC = ({
questionTypeComponent = (
({ ...choice, id: index.toString() }))}
handleOnSubmitAnswer={handleOnSubmitAnswer}
showAnswer={showAnswer}
globalFeedback={question.globalFeedback?.text}
diff --git a/client/src/components/Questions/ShortAnswerQuestion/ShortAnswerQuestion.tsx b/client/src/components/Questions/ShortAnswerQuestion/ShortAnswerQuestion.tsx
index 87d077a..3f134d6 100644
--- a/client/src/components/Questions/ShortAnswerQuestion/ShortAnswerQuestion.tsx
+++ b/client/src/components/Questions/ShortAnswerQuestion/ShortAnswerQuestion.tsx
@@ -10,6 +10,7 @@ type Choices = {
isCorrect: boolean;
text: { format: string; text: string };
weigth?: number;
+ id: string;
};
interface Props {
@@ -33,7 +34,9 @@ const ShortAnswerQuestion: React.FC = (props) => {
<>
{choices.map((choice) => (
-
{choice.text.text}
+
+ {choice.text.text}
+
))}
{globalFeedback && {globalFeedback}
}
diff --git a/client/src/components/ReturnButton/ReturnButton.tsx b/client/src/components/ReturnButton/ReturnButton.tsx
index 0792cfb..a184356 100644
--- a/client/src/components/ReturnButton/ReturnButton.tsx
+++ b/client/src/components/ReturnButton/ReturnButton.tsx
@@ -1,4 +1,5 @@
// GoBackButton.tsx
+import React from 'react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import ConfirmDialog from '../ConfirmDialog/ConfirmDialog';
@@ -33,7 +34,7 @@ const ReturnButton: React.FC = ({
};
const handleOnReturn = () => {
- if (!!onReturn) {
+ if (onReturn) {
onReturn();
} else {
navigate(-1);
diff --git a/client/src/components/StudentWaitPage/StudentWaitPage.tsx b/client/src/components/StudentWaitPage/StudentWaitPage.tsx
index 5937f08..9989b71 100644
--- a/client/src/components/StudentWaitPage/StudentWaitPage.tsx
+++ b/client/src/components/StudentWaitPage/StudentWaitPage.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import { Box, Button, Chip } from '@mui/material';
import { StudentType } from '../../Types/StudentType';
import { PlayArrow } from '@mui/icons-material';
diff --git a/client/src/main.tsx b/client/src/main.tsx
index e4c3824..e73c979 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
@@ -27,10 +28,16 @@ const theme = createTheme({
}
});
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
-
-
-
-);
+const rootElement = document.getElementById('root');
+if (rootElement) {
+
+ ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+ );
+} else {
+ console.error('Root element not found');
+}
diff --git a/client/src/pages/Teacher/Dashboard/Dashboard.tsx b/client/src/pages/Teacher/Dashboard/Dashboard.tsx
index 9fc8cc9..4b9583a 100644
--- a/client/src/pages/Teacher/Dashboard/Dashboard.tsx
+++ b/client/src/pages/Teacher/Dashboard/Dashboard.tsx
@@ -82,7 +82,7 @@ const Dashboard: React.FC = () => {
return;
}
else {
- let userFolders = await ApiService.getUserFolders();
+ const userFolders = await ApiService.getUserFolders();
setFolders(userFolders as FolderType[]);
}
@@ -102,7 +102,7 @@ const Dashboard: React.FC = () => {
if (selectedFolderId == '') {
const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load
console.log("show all quizes")
- var quizzes: QuizType[] = [];
+ let quizzes: QuizType[] = [];
for (const folder of folders as FolderType[]) {
const folderQuizzes = await ApiService.getFolderContent(folder._id);
@@ -155,8 +155,8 @@ const Dashboard: React.FC = () => {
await ApiService.duplicateQuiz(quiz._id);
if (selectedFolderId == '') {
const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load
- console.log("show all quizes")
- var quizzes: QuizType[] = [];
+ console.log("show all quizzes")
+ let quizzes: QuizType[] = [];
for (const folder of folders as FolderType[]) {
const folderQuizzes = await ApiService.getFolderContent(folder._id);
@@ -196,6 +196,7 @@ const Dashboard: React.FC = () => {
// questions[i] = QuestionService.ignoreImgTags(questions[i]);
const parsedItem = parse(questions[i]);
Template(parsedItem[0]);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
return false;
}
@@ -216,7 +217,7 @@ const Dashboard: React.FC = () => {
//const { title, content } = selectedQuiz;
let quizContent = "";
- let title = selectedQuiz.title;
+ const title = selectedQuiz.title;
console.log(selectedQuiz.content);
selectedQuiz.content.forEach((question, qIndex) => {
const formattedQuestion = question.trim();
@@ -273,7 +274,7 @@ const Dashboard: React.FC = () => {
const folders = await ApiService.getUserFolders(); // HACK force user folders to load on first load
console.log("show all quizzes")
- var quizzes: QuizType[] = [];
+ let quizzes: QuizType[] = [];
for (const folder of folders as FolderType[]) {
const folderQuizzes = await ApiService.getFolderContent(folder._id);
diff --git a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
index 15ceeb2..e17c91c 100644
--- a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
+++ b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx
@@ -162,8 +162,10 @@ const QuizForm: React.FC = () => {
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`)
+
}
};
@@ -245,7 +247,7 @@ const QuizForm: React.FC = () => {
onClose={() => setDialogOpen(false)} >
Erreur
- Veuillez d'abord choisir une image à téléverser.
+ Veuillez d'abord choisir une image à téléverser.
setDialogOpen(false)} color="primary">
diff --git a/client/src/pages/Teacher/Login/Login.tsx b/client/src/pages/Teacher/Login/Login.tsx
index fe99d8b..8e914b8 100644
--- a/client/src/pages/Teacher/Login/Login.tsx
+++ b/client/src/pages/Teacher/Login/Login.tsx
@@ -28,7 +28,7 @@ const Login: React.FC = () => {
const login = async () => {
const result = await ApiService.login(email, password);
- if (result != true) {
+ if (typeof result === "string") {
setConnectionError(result);
return;
}
diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
index 949a6de..73ac6e0 100644
--- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
+++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx
@@ -124,8 +124,8 @@ const ManageRoom: React.FC = () => {
// This is here to make sure the correct value is sent when user join
if (socket) {
console.log(`Listening for user-joined in room ${roomName}`);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
socket.on('user-joined', (_student: StudentType) => {
-
if (quizMode === 'teacher') {
webSocketService.nextQuestion(roomName, currentQuestion);
} else if (quizMode === 'student') {
diff --git a/client/src/pages/Teacher/Register/Register.tsx b/client/src/pages/Teacher/Register/Register.tsx
index 29d5f2b..76a683c 100644
--- a/client/src/pages/Teacher/Register/Register.tsx
+++ b/client/src/pages/Teacher/Register/Register.tsx
@@ -28,7 +28,7 @@ const Register: React.FC = () => {
const register = async () => {
const result = await ApiService.register(email, password);
- if (result != true) {
+ if (typeof result === 'string') {
setConnectionError(result);
return;
}
@@ -70,7 +70,7 @@ const Register: React.FC = () => {
sx={{ marginBottom: `${connectionError && '2rem'}` }}
disabled={!email || !password}
>
- S'inscrire
+ S'inscrire
diff --git a/client/src/pages/Teacher/ResetPassword/ResetPassword.tsx b/client/src/pages/Teacher/ResetPassword/ResetPassword.tsx
index 4901de1..27fe796 100644
--- a/client/src/pages/Teacher/ResetPassword/ResetPassword.tsx
+++ b/client/src/pages/Teacher/ResetPassword/ResetPassword.tsx
@@ -27,7 +27,7 @@ const ResetPassword: React.FC = () => {
const reset = async () => {
const result = await ApiService.resetPassword(email);
- if (result != true) {
+ if (typeof result === 'string') {
setConnectionError(result);
return;
}
diff --git a/client/src/services/ApiService.tsx b/client/src/services/ApiService.tsx
index 987c091..4893c32 100644
--- a/client/src/services/ApiService.tsx
+++ b/client/src/services/ApiService.tsx
@@ -4,6 +4,8 @@ import { FolderType } from 'src/Types/FolderType';
import { QuizType } from 'src/Types/QuizType';
import { ENV_VARIABLES } from 'src/constants';
+type ApiResponse = boolean | string;
+
class ApiService {
private BASE_URL: string;
private TTL: number;
@@ -17,7 +19,7 @@ class ApiService {
return `${this.BASE_URL}/api${endpoint}`;
}
- private constructRequestHeaders(): any {
+ private constructRequestHeaders() {
if (this.isLoggedIn()) {
return {
Authorization: `Bearer ${this.getToken()}`,
@@ -86,7 +88,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async register(email: string, password: string): Promise {
+ public async register(email: string, password: string): Promise {
try {
if (!email || !password) {
@@ -122,7 +124,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async login(email: string, password: string): Promise {
+ public async login(email: string, password: string): Promise {
try {
if (!email || !password) {
@@ -146,8 +148,13 @@ class ApiService {
} catch (error) {
console.log("Error details: ", error);
+ console.log("axios.isAxiosError(error): ", axios.isAxiosError(error));
+
if (axios.isAxiosError(error)) {
const err = error as AxiosError;
+ if (err.status === 401) {
+ return 'Email ou mot de passe incorrect.';
+ }
const data = err.response?.data as { error: string } | undefined;
return data?.error || 'Erreur serveur inconnue lors de la requête.';
}
@@ -157,10 +164,10 @@ class ApiService {
}
/**
- * @returns true if successful
+ * @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async resetPassword(email: string): Promise {
+ public async resetPassword(email: string): Promise {
try {
if (!email) {
@@ -196,7 +203,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async changePassword(email: string, oldPassword: string, newPassword: string): Promise {
+ public async changePassword(email: string, oldPassword: string, newPassword: string): Promise {
try {
if (!email || !oldPassword || !newPassword) {
@@ -232,7 +239,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async deleteUser(email: string, password: string): Promise {
+ public async deleteUser(email: string, password: string): Promise {
try {
if (!email || !password) {
@@ -270,7 +277,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async createFolder(title: string): Promise {
+ public async createFolder(title: string): Promise {
try {
if (!title) {
@@ -375,7 +382,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async deleteFolder(folderId: string): Promise {
+ public async deleteFolder(folderId: string): Promise {
try {
if (!folderId) {
@@ -410,7 +417,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async renameFolder(folderId: string, newTitle: string): Promise {
+ public async renameFolder(folderId: string, newTitle: string): Promise {
try {
if (!folderId || !newTitle) {
@@ -441,7 +448,7 @@ class ApiService {
}
}
- public async duplicateFolder(folderId: string): Promise {
+ public async duplicateFolder(folderId: string): Promise {
try {
if (!folderId) {
throw new Error(`Le folderId et le nouveau titre sont requis.`);
@@ -473,7 +480,7 @@ class ApiService {
}
}
- public async copyFolder(folderId: string, newTitle: string): Promise {
+ public async copyFolder(folderId: string, newTitle: string): Promise {
try {
if (!folderId || !newTitle) {
throw new Error(`Le folderId et le nouveau titre sont requis.`);
@@ -510,7 +517,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async createQuiz(title: string, content: string[], folderId: string): Promise {
+ public async createQuiz(title: string, content: string[], folderId: string): Promise {
try {
if (!title || !content || !folderId) {
@@ -581,7 +588,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async deleteQuiz(quizId: string): Promise {
+ public async deleteQuiz(quizId: string): Promise {
try {
if (!quizId) {
@@ -616,7 +623,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async updateQuiz(quizId: string, newTitle: string, newContent: string[]): Promise {
+ public async updateQuiz(quizId: string, newTitle: string, newContent: string[]): Promise {
try {
if (!quizId || !newTitle || !newContent) {
@@ -652,7 +659,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async moveQuiz(quizId: string, newFolderId: string): Promise {
+ public async moveQuiz(quizId: string, newFolderId: string): Promise {
try {
if (!quizId || !newFolderId) {
@@ -689,7 +696,7 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async duplicateQuiz(quizId: string): Promise {
+ public async duplicateQuiz(quizId: string): Promise {
const url: string = this.constructRequestUrl(`/quiz/duplicate`);
@@ -703,7 +710,7 @@ class ApiService {
throw new Error(`La duplication du quiz a échoué. Status: ${result.status}`);
}
- return result;
+ return result.status === 200;
} catch (error) {
console.error("Error details: ", error);
@@ -723,9 +730,9 @@ class ApiService {
* @returns true if successful
* @returns A error string if unsuccessful,
*/
- public async copyQuiz(quizId: string, newTitle: string, folderId: string): Promise {
+ public async copyQuiz(quizId: string, newTitle: string, folderId: string): Promise {
try {
- console.log(quizId, newTitle), folderId;
+ console.log(quizId, newTitle, folderId);
return "Route not implemented yet!";
} catch (error) {
@@ -741,7 +748,7 @@ class ApiService {
}
}
- async ShareQuiz(quizId: string, email: string): Promise {
+ async ShareQuiz(quizId: string, email: string): Promise {
try {
if (!quizId || !email) {
throw new Error(`quizId and email are required.`);
@@ -800,7 +807,7 @@ class ApiService {
}
}
- async receiveSharedQuiz(quizId: string, folderId: string): Promise {
+ async receiveSharedQuiz(quizId: string, folderId: string): Promise {
try {
if (!quizId || !folderId) {
throw new Error(`quizId and folderId are required.`);
@@ -869,7 +876,8 @@ class ApiService {
if (axios.isAxiosError(error)) {
const err = error as AxiosError;
const data = err.response?.data as { error: string } | undefined;
- return `ERROR : ${data?.error}` || 'ERROR : Erreur serveur inconnue lors de la requête.';
+ const msg = data?.error || 'Erreur serveur inconnue lors de la requête.';
+ return `ERROR : ${msg}`;
}
return `ERROR : Une erreur inattendue s'est produite.`
diff --git a/client/src/utils/giftUtils.ts b/client/src/utils/giftUtils.ts
index f81bda8..5eb88e9 100644
--- a/client/src/utils/giftUtils.ts
+++ b/client/src/utils/giftUtils.ts
@@ -1,4 +1,4 @@
export function escapeForGIFT(link: string): string {
- const specialChars = /[{}#~=<>\:]/g;
+ const specialChars = /[{}#~=<>\\:]/g;
return link.replace(specialChars, (match) => `\\${match}`);
}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index d328c26..a6b7592 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -16,7 +16,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
- "jsx": "react-jsx",
+ "jsx": "react",
/* Linting */
"strict": true,
diff --git a/server/__tests__/folders.test.js b/server/__tests__/folders.test.js
index 8e4bbdd..84a7c7f 100644
--- a/server/__tests__/folders.test.js
+++ b/server/__tests__/folders.test.js
@@ -1,4 +1,3 @@
-const { create } = require('../middleware/jwtToken');
const Folders = require('../models/folders');
const ObjectId = require('mongodb').ObjectId;
const Quizzes = require('../models/quiz');
diff --git a/server/__tests__/image.test.js b/server/__tests__/image.test.js
index 41308e7..488d27b 100644
--- a/server/__tests__/image.test.js
+++ b/server/__tests__/image.test.js
@@ -1,3 +1,5 @@
+/* eslint-disable */
+
// const request = require('supertest');
// const app = require('../app.js');
// // const app = require('../routers/images.js');
diff --git a/server/__tests__/users.test.js b/server/__tests__/users.test.js
index 21c6dfa..2c6b4eb 100644
--- a/server/__tests__/users.test.js
+++ b/server/__tests__/users.test.js
@@ -2,7 +2,6 @@ const Users = require('../models/users');
const bcrypt = require('bcrypt');
const Quizzes = require('../models/quiz');
const Folders = require('../models/folders');
-const AppError = require('../middleware/AppError');
const { ObjectId } = require('mongodb');
jest.mock('bcrypt');
diff --git a/server/constants/errorCodes.js b/server/constants/errorCodes.js
index d7ca180..fb691f8 100644
--- a/server/constants/errorCodes.js
+++ b/server/constants/errorCodes.js
@@ -18,7 +18,7 @@ exports.USER_ALREADY_EXISTS = {
}
exports.LOGIN_CREDENTIALS_ERROR = {
message: 'L\'email et le mot de passe ne correspondent pas.',
- code: 400
+ code: 401
}
exports.GENERATE_PASSWORD_ERROR = {
message: 'Une erreur s\'est produite lors de la création d\'un nouveau mot de passe.',
@@ -130,4 +130,4 @@ exports.NOT_IMPLEMENTED = {
// static badRequest(res, message) {400
// static unauthorized(res, message) {401
// static notFound(res, message) {404
-// static serverError(res, message) {505
\ No newline at end of file
+// static serverError(res, message) {505
diff --git a/server/controllers/folders.js b/server/controllers/folders.js
index 1b0c1b3..9858821 100644
--- a/server/controllers/folders.js
+++ b/server/controllers/folders.js
@@ -1,6 +1,6 @@
//controller
const AppError = require('../middleware/AppError.js');
-const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
+const { MISSING_REQUIRED_PARAMETER, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
class FoldersController {
diff --git a/server/controllers/quiz.js b/server/controllers/quiz.js
index e293bf6..77f7953 100644
--- a/server/controllers/quiz.js
+++ b/server/controllers/quiz.js
@@ -1,13 +1,13 @@
const emailer = require('../config/email.js');
const AppError = require('../middleware/AppError.js');
-const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR, DUPLICATE_QUIZ_ERROR, COPY_QUIZ_ERROR } = require('../constants/errorCodes');
+const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR } = require('../constants/errorCodes');
class QuizController {
constructor(quizModel, foldersModel) {
- this.folders = foldersModel;
this.quizzes = quizModel;
+ this.folders = foldersModel;
}
create = async (req, res, next) => {
@@ -165,7 +165,7 @@ class QuizController {
}
};
- copy = async (req, res, next) => {
+ copy = async (req, _res, _next) => {
const { quizId, newTitle, folderId } = req.body;
if (!quizId || !newTitle || !folderId) {
@@ -207,7 +207,7 @@ class QuizController {
}
// Call the method from the Quiz model to delete quizzes by folder ID
- await Quiz.deleteQuizzesByFolderId(folderId);
+ await this.quizzes.deleteQuizzesByFolderId(folderId);
return res.status(200).json({
message: 'Quizzes deleted successfully.'
@@ -232,7 +232,7 @@ class QuizController {
try {
const existingFile = await this.quizzes.quizExists(title, userId);
return existingFile !== null;
- } catch (error) {
+ } catch (_error) {
throw new AppError(GETTING_QUIZ_ERROR);
}
};
diff --git a/server/eslint.config.mjs b/server/eslint.config.mjs
new file mode 100644
index 0000000..da499fe
--- /dev/null
+++ b/server/eslint.config.mjs
@@ -0,0 +1,32 @@
+import globals from "globals";
+import pluginJs from "@eslint/js";
+
+/** @type {import('eslint').Linter.Config[]} */
+export default [
+ {
+ files: ["**/*.js"],
+ languageOptions: {
+ sourceType: "commonjs",
+ globals: {
+ ...globals.node,
+ ...globals.jest, // Add Jest globals
+ },
+ },
+ rules: {
+ "no-unused-vars": ["error", {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_",
+ "caughtErrors": "all", // Ignore all catch clause parameters
+ "caughtErrorsIgnorePattern": "^_" // Ignore catch clause parameters that start with _
+ }],
+ },
+ },
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ },
+ },
+ },
+ pluginJs.configs.recommended,
+];
diff --git a/server/middleware/errorHandler.js b/server/middleware/errorHandler.js
index ddddeb2..61fede6 100644
--- a/server/middleware/errorHandler.js
+++ b/server/middleware/errorHandler.js
@@ -1,7 +1,7 @@
const AppError = require("./AppError");
const fs = require('fs');
-const errorHandler = (error, req, res, next) => {
+const errorHandler = (error, req, res) => {
console.log("ERROR", error);
if (error instanceof AppError) {
diff --git a/server/models/images.js b/server/models/images.js
index a80b3f0..67a6583 100644
--- a/server/models/images.js
+++ b/server/models/images.js
@@ -1,4 +1,3 @@
-const db = require('../config/db.js')
const { ObjectId } = require('mongodb');
class Images {
@@ -8,8 +7,8 @@ class Images {
}
async upload(file, userId) {
- await db.connect()
- const conn = db.getConnection();
+ await this.db.connect()
+ const conn = this.db.getConnection();
const imagesCollection = conn.collection('images');
@@ -27,8 +26,8 @@ class Images {
}
async get(id) {
- await db.connect()
- const conn = db.getConnection();
+ await this.db.connect()
+ const conn = this.db.getConnection();
const imagesCollection = conn.collection('images');
diff --git a/server/package-lock.json b/server/package-lock.json
index 644d9ea..9b44c36 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -21,7 +21,10 @@
"socket.io-client": "^4.7.2"
},
"devDependencies": {
+ "@eslint/js": "^9.18.0",
"cross-env": "^7.0.3",
+ "eslint": "^9.18.0",
+ "globals": "^15.14.0",
"jest": "^29.7.0",
"jest-mock": "^29.7.0",
"nodemon": "^3.0.1",
@@ -677,6 +680,16 @@
}
}
},
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/@babel/traverse/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -703,6 +716,283 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
+ "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
+ "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.5",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/config-array/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
+ "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
+ "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
+ "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
+ "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
+ "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.10.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
+ "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1239,6 +1529,13 @@
"@types/node": "*"
}
},
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -1272,6 +1569,13 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "20.8.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz",
@@ -1331,6 +1635,29 @@
"node": ">= 0.6"
}
},
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -1363,6 +1690,23 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -2122,10 +2466,11 @@
}
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -2157,6 +2502,13 @@
}
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -2445,6 +2797,230 @@
"node": ">=8"
}
},
+ "node_modules/eslint": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
+ "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.0",
+ "@eslint/core": "^0.10.0",
+ "@eslint/eslintrc": "^3.2.0",
+ "@eslint/js": "9.18.0",
+ "@eslint/plugin-kit": "^0.2.5",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.1",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -2458,6 +3034,52 @@
"node": ">=4"
}
},
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -2515,9 +3137,10 @@
}
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -2538,7 +3161,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -2553,14 +3176,32 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
@@ -2576,6 +3217,19 @@
"bser": "2.1.1"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -2618,6 +3272,27 @@
"node": ">=8"
}
},
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -2820,12 +3495,16 @@
}
},
"node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "version": "15.14.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
+ "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gopd": {
@@ -2986,12 +3665,49 @@
"node": ">=0.10.0"
}
},
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -3941,12 +4657,33 @@
"node": ">=4"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
},
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -4004,6 +4741,16 @@
"safe-buffer": "^5.0.1"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -4022,6 +4769,20 @@
"node": ">=6"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -4070,6 +4831,13 @@
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -4565,6 +5333,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -4601,6 +5387,19 @@
"node": ">=6"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -4660,9 +5459,10 @@
"dev": true
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "license": "MIT"
},
"node_modules/picocolors": {
"version": "1.0.0",
@@ -4703,6 +5503,16 @@
"node": ">=8"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -5553,6 +6363,19 @@
"node": ">=14"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@@ -5640,6 +6463,16 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -5727,6 +6560,16 @@
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
diff --git a/server/package.json b/server/package.json
index d3e8919..c659597 100644
--- a/server/package.json
+++ b/server/package.json
@@ -25,7 +25,10 @@
"socket.io-client": "^4.7.2"
},
"devDependencies": {
+ "@eslint/js": "^9.18.0",
"cross-env": "^7.0.3",
+ "eslint": "^9.18.0",
+ "globals": "^15.14.0",
"jest": "^29.7.0",
"jest-mock": "^29.7.0",
"nodemon": "^3.0.1",