diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0da3f793e1ef5cbd7e8c253696632ae38bbba403..f5bbf3b32a4fe8f8126c1d7b69806cd2fcee8ddf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,14 @@
 - No modifications / Keep major and minors versions in sync with all parts of the project
 -->
 
-## 3.0.0 (?)
+
+## 3.1.0 (???)
+
+### 🔨 Internal / Developers
+- **Typescript**: Add linter (ESLint)
+
+
+## 3.0.0 (2023-11-03)
 
 ### 🔨 Internal / Developers
 - No modifications / Keep major and minors versions in sync with all parts of the project
diff --git a/ExerciseChecker/.env.vault b/ExerciseChecker/.env.vault
index 2303c6a03102e88789ca87a6ac638fde4d4138f9..778640b68e2a672e519a3c195010408bea64e26c 100644
--- a/ExerciseChecker/.env.vault
+++ b/ExerciseChecker/.env.vault
@@ -4,8 +4,11 @@
 #/--------------------------------------------------/
 
 # development
-DOTENV_VAULT_DEVELOPMENT="1IL1le/MSmLP5ikyD+SN+O1/lxN+d3xvFqrAKxAd9Kil5QJwZ6QBHB2PshSK4velr89Es2J05xYjjrxm5bCavNiHK6ZEPn0lgvQBRkEuZB+xm728lYE684Uij98v10K5ZyOGW6jlXzbfytnexit4QZOFILTpJEZzyXZYP+PjW1fa5ecs++Pml76UTsIuyw7lFvJ6x0oeMljbaUIZGys5MLQPz8NepwmZNKu8qG5ld6jZJ47rvSvjxJQkUPwmHkxPcuxztetdkIBOYdXvbhR3FKpxQlAosdq1YaznQooZdj1PCSBmLE/217M+l4yCUPfj3fVMWIzlcLuDdwjfq5G3E82LIBi5L7zPFaQZE4DkaKZRO5utpF6DliGSueRV/+Rf/sJGjCbAyxvSaofkn3EZwbSMqfDW5kDpQzFfdPOoCUNYu3tTqrTNeTY7lgurIjidaqHkZQS1rau8cU+6J3C0GB34amREQx5crrf/obeEfkBtpZKTZG7+a5u5ZGcp+roiIqAFhOMtbh/aqpqmLcGdTtRRmHoOCV+YLb6q1YZiJNA/JIqGTrQncj4KeRIsgnhENFQa0MQ8U7ROrKICalSY0Vg4B/EMALFadi2oXA57+FVWO1DnjErYKFrmfVBW2+obYovrB95oE4QhTHTu/HTW1r7YNZUAAtLGMguqfbAjrhL7jVbduKeo7BRGJmxHsv4UVMeUoiRgKpd5Ig/t9y7+feSaKC1KeDeBgBD6IpfM9hl9LcyI/SEKML0O5oZORXvc8L7vBBB+ylFfS/LfehCzdLbcqSVog1YgVQAPg7wD+8m/91BgEqy7aCdDT4K+U9kDx8hECDTohcooBH3NVz2F5QT9mxOvlZ+ImG3VANcH3sm4CcGvHHPapervtvNjBApr6F4xW7KHiCUr65X5yQ1ilHg7Bkf2+MthCaQHG0Jo5Fwb3nk3Sp0SUmB/YZn/0ruOQ8cKTHkSAb8y+MXCFpC4L/0gBYsEPWgvAi5S2FvOeVZiIYMvxHYxulqRrErl2euLVrGCHoFdxEQdJ92AlyEYztYQG/dR2oRRYlhw/tRCPpZZzfGvAaI7FgmKIppkzt8="
+DOTENV_VAULT_DEVELOPMENT="5g8bGHO2gv3YgdpmW5PvpWORmnu6pppW99g16TX37S81eA4Dnl0CfI0ljVAkDQ45u6lUUUFZEGfFE2x9Lgg+zENxfkVvZAkzFj+0rRKaxipqSj97taT/tVJbXq3M5cmlVwQNP9AKJMLF6/+r/+MPdhkGHU/yM/5/ycmZZoPqfHAmt6KRoTyRMBPH3/kR/7PK5lRpxf4iLSewOD5tBz60nI3GF61mpRFhIloTMsnaab65wERGwA32GS6JKU3RRaQVyyLi/d2kUfFTrq3HN7d37ZN0iNqPv556/ZNs9vmHfgjlNR9SYytzVgyVLKzRlvUpp1mz4tDDpA3OgqV5nymwQ8uSKXuvfhVnh5ac4Nf+v6xcgukt6ps2VEU5VR/qn0nwBX1zWR+K0d/lZxuKtQxVAwUq3Qhwfd8xgmm9iDmW9CqcgXp2SOsRnUaWmLcqgrlfPEJle/EVBDDL/OgzVqky/ZU0nZSJf5W9KAHGXm+CbXX7c7HljMACQdU64wvoI4CNhZ/SJvbdjSBuK45Y08FIv0G7hg52+7tA/qx/p8iST6a4Glqtwvh1TOt8ezEM0UATVy+26EnK44WRku7Xv1ZuLAyHEfmLVxsIeXXzyIHft8Iy3A1Lf4Hm4jcNaYap0DvWPREMTwGXb7FkgTSpdUfYCB2ctdl1Ndva7NDWQwuIC38/+fn5JouB6jyHRB6SfLl/YIz9NeNGPSHRofO46qI1HX2LTZ312ns3wv/8+gOsRmgwBtKuqZ2OqvQYqEnc+372n3MdkRTVIPM+QjvEAkInVtIZY9cWT1aSMFfXEyekDAVxxVKrlzYu1cogwbdMleeTz9XU0qzsghp6ftwox79aStcjCcJHiMTOuz+HWm6MVq0lFwsDvZQVSCYgjkoZBUWvS54c6zcumkbBJF5szyOJrVsiU8QCq47zaQ1O+WIKq1QFhgZm6W2vq7sc/lMPH/gDV2U5O3NpVA0NOo3OBRezkmeX3R7ElVmvHYZxnTtTcepep+5XJkdzrDVuSJWdVM8MWzh16YiefFYD5gxWG6tp8QVM+BXLS/5ErBpKc+40ReBGAL+9spiBxAAgwog4WkKt2jklOCKqQWa3dTcKCqi+g5TwvpOGLmgNg9wg2T5ZR5+W+U4A8bKxSbUdFjYlv7Jxje2ipmbfI3o515xFEiFYc0gPgGSBqg=="
 
 # production
-DOTENV_VAULT_PRODUCTION="AkhlY5isBoAxVj632ISmuP6gPrB1u1DfsPUIw64HlEl1pr7xSsdQlyemBukr0XlKoE+IKBFy6hfVgqWvxe48BlCzMx41xVjfp5NoZGTXsJGgqlPa3NJDdWTTgSn68Q11TVagGOCQjQnRVjAV6NQFdwP1aKJ8/lg77cPlKrlWRphzAG0hHWWFecxOVs6DoMZPtHW6mabBiyC8MlBRhd4vqV84H1nbYlkSPzEcd9VRhZGLoA3uTHXKnXDoMeNKSK5Z9PxL/PmM/3duXKi/3NNvksLdAJlKGF9Q1sLQpWVDKtjoaKf5GumanxBXMdp5Gt5s0tf1TNAKkj9l6+HYN8jBkhVEh/OiHzMMhvMDDo+ZwhL6U8z0aTK+W8Y8KmGQ0BYp0uEtFSaxa95zJ18YBbs8FPLlUAvBlOlt5F7Z8tSkaSCcC3PNMRsnnzbwJquIh5AyS5scDqsvkdKmfSWb+LLdaY6TLa4conh5iFKkxe5GQ9JmaTGPBHzvhxJRJkUTZcpM5yPCizjFsa51E+FQqk+heI1Y0oAlL69xw+DOyNhAYU/rZJDkGyi7X9JZjgvpNROi1M1h72fO5/uz47mvlxzzg45w4nwk3k3R7azXzzdWsznPcEXu74LAfXC4+SRZuLEOtBbzHRLgM2WIo+CTOuqZGMPSqWhUWFmeCUL+9oON3K93IuvBB+ImtMKxqZIUevzlvv+yaKHod9PjOnpQ1FjKmgM9HZPsrcF/Y6Em98ckVaBpNJztfdVjsihVZGrpr2xlO9RmaQ=="
+DOTENV_VAULT_PRODUCTION="7aZnWWflHA4gs5ZZHDpwkeHeKvDMDReEkKFaaVknWI1g7V3mBztxc+26BlfLvAhQxNPRTt5Rkqybf6g4B8lvy0sTlR3qQoM4AYXiNZe4L/GGeefozJnGAd5hUBLO9Chqu2C22cU7Xe4dHcHV08Fxnu8aKL9iTwR5a3PvrNlpIeNIIyoBMLDDKpdhhj7ru4Jv/bsj5Jr7+yddkUg5538+gVnFxT34cOoWAEgibXRpAbEdZxeHn1AwoIAz7ToRbDllcwc9QPhpRHLIg+QsubLZ8eLCrzeUmMWJA/VWvOvH7ZowlbewHpc/W5MyIoiUlXztrroTe2MDYySyYgCK2z8aDpvEJ5wm0WPAzz+UTmsIOJ4QD86kKuXrj8BEVkxX6buNbDcl2yJ/1laobsjSLQHjfGReElP241JKhH4wgkcbsEtAgCfX9VzJ+aqHErIVlXlQW4uLHiTNjo3pHJAfvMJ3rzq/OgdX/4s/5eebQieNbvPUKznJyhYzsSmrj2OUL/A9Cl0pz8NvD6Vw0/ujC7hI36HlZHLI09/4dBKzlUS2oRhSnSB0SIwh0TY0I3jHz+PSqv2cUQcbU19ZcT0IvW7jhTb3gWNzYb839OEjs8VYM2sTSOezKswJpu5sV4NqUONwHhvGkJsAHaFZ1IPpW5kyUCHDkjoD9zFiVo5676XLGjt+fhb+XGXoFDnNpVc1OCGCrDsKMtoCRRpC3eOGrE9bvX6oF3BEhhheUefRYG649PjZNbJNvD5wrrprzahhjQvq6bA0rA=="
+
+# test
+DOTENV_VAULT_TEST="slPyHq6uj365knunmDQbHELis6HUmi/5/n2WG6GiRVskNBYYFP1Hh7vMo52K23KHw2DV3cVM3wlDM4OKSCErqN8NOUukB0zIWhjOnio7U6eXzcsZcK0i2cCyH292zfnrKdWjzxecLgI+/nPX8gb5/0iQTr/c1dhLf3t24jfQZO8P4LGBYdvetrgnaMedRy/k6dDzO4Yxl33Oa9sQCwsL0OtUkfF9k6pE6Z89j/GiO3RRGOiM1XrMkUk/XojMYdXQkRpRPm5YW8i9tEpwPiH8QP/cqHfSz8IL3BKBVOeSoYaJb7NbvF4yhxt5d990Ugu1geYISrrLXhw2z4g2oNRS8YK3nraaXJtlMqTYUZJMoAGBtqhwrdfn33VqQJDbwmtaQsDl8lQ94kJvODeThxQdniS90sUe6WYVsUKdKj48I+JiSiTjJfYS29GOhKvPsZz8CszNiVu5ib6IiZgEFtvl6s+/I143HaahQLtGv6ZynYk7bUuRGd0jaiZR+J2o/rzPMeRCdEOxpByEAs302zZjVnxV582IinG1jRGvu27/BqDyn1j5p3jIJNL9AX5ptuoi0cC3kuJMzALWa51MbD8UOv5Tq68lxDnuG1k77f0QaLsrzdzbJwTpqmJPN3MFYzTQWyqBLUSA32eW0bablKR+YXcvfR6/3ikRofDE10bJYVqX5Ene1DaBXMZwQpaesnto46w+QBL0GRqkvAzQcdtWkl2ABuqRYBB4sWnZHMpbopspwsbV/EdwlcXdwNXHQf2z4m5k8f4Uh6toSwzn"
 
diff --git a/ExerciseChecker/.eslintignore b/ExerciseChecker/.eslintignore
new file mode 100644
index 0000000000000000000000000000000000000000..ecded56c0bd829718e46ffea13b1e3b6791c794e
--- /dev/null
+++ b/ExerciseChecker/.eslintignore
@@ -0,0 +1,3 @@
+dist
+node_modules
+.gitlab-ci
\ No newline at end of file
diff --git a/ExerciseChecker/.eslintrc.json b/ExerciseChecker/.eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..be8c02d07111eec0c73437a65326e56150fda24d
--- /dev/null
+++ b/ExerciseChecker/.eslintrc.json
@@ -0,0 +1,11 @@
+{
+    "root"   : true,
+    "parser" : "@typescript-eslint/parser",
+    "plugins": [
+        "@typescript-eslint"
+    ],
+    "extends": [
+        "eslint:recommended",
+        "plugin:@typescript-eslint/recommended"
+    ]
+}
\ No newline at end of file
diff --git a/ExerciseChecker/.gitlab-ci/00_vars.yml b/ExerciseChecker/.gitlab-ci/00_vars.yml
index 46cb27b058b7ab53199e24bc6288172dcdf94417..1fcea071b2c22d5d88c35c4a42f7515a42949095 100644
--- a/ExerciseChecker/.gitlab-ci/00_vars.yml
+++ b/ExerciseChecker/.gitlab-ci/00_vars.yml
@@ -6,9 +6,14 @@ variables:
     DOCKER_TLS_CERTDIR:
     DOCKER_DRIVER: overlay2
 
+    DOCKER_REGISTRY: docker.io
+    DOCKER_REGISTRY_USER: dojohesso
+    DOCKER_REGISTRY_IMAGE: dojohesso/dojo_exercise_checker
+
     PROJECT_NAME: DojoExerciseChecker
 
     VERSION_DEV_SUFFIX: '-dev'
+    VERSION_TEST_SUFFIX: '-test'
 
     GITLAB_API_PROJECT_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}
 
diff --git a/ExerciseChecker/.gitlab-ci/01_functions.yml b/ExerciseChecker/.gitlab-ci/01_functions.yml
index f0675a514e20658a7d87932f65073487c89cc028..05fe3aec03b9723a1d71575ff6d6412cf57809c5 100644
--- a/ExerciseChecker/.gitlab-ci/01_functions.yml
+++ b/ExerciseChecker/.gitlab-ci/01_functions.yml
@@ -1,7 +1,19 @@
+.get_vars:
+    script:
+        - IS_DEV=$([[ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_COMMIT_BRANCH != "test" ]] && echo true || echo false)
+        - IS_TEST=$([[ $CI_COMMIT_BRANCH == "test" ]] && echo true || echo false)
+        - |
+            if [[ $IS_TEST == true ]]; then
+                DOCKER_REGISTRY_USER="dojohessotest"
+                DOCKER_REGISTRY_IMAGE="dojohesso/dojo-test_exercise_checker"
+                DOCKER_REGISTRY_PASSWORD=$DOCKER_TEST_REGISTRY_PASSWORD
+                DOTENV_PROD_KEY=$DOTENV_TEST_KEY
+            fi
+
+
 .get_version:
     script:
-        - IS_DEV=$([[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] && echo false || echo true)
-        - VERSION=$(jq -r .version $PROJECT_FOLDER/package.json)$([[ $IS_DEV == true ]] && echo $VERSION_DEV_SUFFIX || echo '')
+        - VERSION=$(jq -r .version $PROJECT_FOLDER/package.json)$([[ $IS_TEST == true ]] && echo $VERSION_TEST_SUFFIX || echo '' )$([[ $IS_DEV == true ]] && echo $VERSION_DEV_SUFFIX || echo '')
 
 
 .get_packages_url:
@@ -21,7 +33,7 @@
 
         # Init docker buildx
         - |
-            if [ $CI_COMMIT_REF_PROTECTED == "true" ]; then
+            if [[ $CI_COMMIT_REF_PROTECTED == "true" || $IS_TEST == true ]]; then
                 docker login -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY
             fi
         - docker buildx create --use
@@ -41,15 +53,20 @@
         - cd $PROJECT_FOLDER
         - sed -i -r "s/\{\{VERSION\}\}/${VERSION}/g" src/app.ts
         - |
-            if [ $CI_COMMIT_REF_PROTECTED == "true" ]; then
+            if [[ $CI_COMMIT_REF_PROTECTED == "true" || $IS_TEST == true ]]; then
                 echo "Decrypt production env vars"
                 sed -i -r "s/(DOTENV_KEY[ ]*:[ ]*[\'\"\`])[^'\"\`]*([\'\"\`])([ ]*\,)?//g" src/app.ts
                 sed -i -r "s/,[\ \n]*\}/\}/g" src/app.ts
                 npx dotenv-vault local decrypt "${DOTENV_PROD_KEY}" > .env
-            
             fi
         - cd ..
 
+        # Rename executable for test
+        - |
+            if [[ $IS_TEST == true ]]; then
+                sed -i -r "s%dojo_exercise_checker%dojo-test_exercise_checker%g" $DOCKERFILE
+            fi
+
         # Need to build for each platform separately because of multi-stage builds (docker buildx don't use cache same way as docker build)
         - >
             platform_array=$(echo $DOCKER_PLATFORMS | tr "," "\n");
diff --git a/ExerciseChecker/.gitlab-ci/03_stages.yml b/ExerciseChecker/.gitlab-ci/03_stages.yml
index 1b20f1ed1fa05c19ee6d8145ccd33bcc39c85d9d..a996ecac4ea23db0640d1c6767b1780748aeccbe 100644
--- a/ExerciseChecker/.gitlab-ci/03_stages.yml
+++ b/ExerciseChecker/.gitlab-ci/03_stages.yml
@@ -1,6 +1,10 @@
 stages:
+    - code_quality
     - test
     - build
     - clean
     - upload
-    - release
\ No newline at end of file
+    - release
+
+before_script:
+    - !reference [ .get_vars, script ]
\ No newline at end of file
diff --git a/ExerciseChecker/.gitlab-ci/04_stageCodeQuality.yml b/ExerciseChecker/.gitlab-ci/04_stageCodeQuality.yml
new file mode 100644
index 0000000000000000000000000000000000000000..48f2394902f9143be7eee5ca5706ffbd65f3355b
--- /dev/null
+++ b/ExerciseChecker/.gitlab-ci/04_stageCodeQuality.yml
@@ -0,0 +1,12 @@
+code_quality:lint:
+    stage: code_quality
+    tags:
+        - code_quality
+    image: node:latest
+    script:
+        - cd "${PROJECT_FOLDER}"
+        
+        - npm install
+        - npm run lint
+    rules:
+        -   if: '$CI_COMMIT_TAG =~ "/^$/"'
diff --git a/ExerciseChecker/.gitlab-ci/04_stageTest.yml b/ExerciseChecker/.gitlab-ci/05_stageTest.yml
similarity index 84%
rename from ExerciseChecker/.gitlab-ci/04_stageTest.yml
rename to ExerciseChecker/.gitlab-ci/05_stageTest.yml
index a1b564295624124e216ae7bba18ac6950590eccb..ccb013bcc6113315e83da6b2190787d73ee11903 100644
--- a/ExerciseChecker/.gitlab-ci/04_stageTest.yml
+++ b/ExerciseChecker/.gitlab-ci/05_stageTest.yml
@@ -9,4 +9,4 @@ test:build:
     script:
         - !reference [ .build_script, script ]
     rules:
-        -   if: '$CI_COMMIT_TAG =~ "/^$/" && $CI_COMMIT_REF_PROTECTED != "true"'
\ No newline at end of file
+        -   if: '$CI_COMMIT_TAG =~ "/^$/" && $CI_COMMIT_REF_PROTECTED != "true" && $CI_COMMIT_BRANCH != "test"'
\ No newline at end of file
diff --git a/ExerciseChecker/.gitlab-ci/05_stageBuild.yml b/ExerciseChecker/.gitlab-ci/06_stageBuild.yml
similarity index 82%
rename from ExerciseChecker/.gitlab-ci/05_stageBuild.yml
rename to ExerciseChecker/.gitlab-ci/06_stageBuild.yml
index e2acdd1042c77d84b45e63e3b8b2cdaf217d92bc..a0c6ca411fd86c59aa49fd601563bd262e3ef89f 100644
--- a/ExerciseChecker/.gitlab-ci/05_stageBuild.yml
+++ b/ExerciseChecker/.gitlab-ci/06_stageBuild.yml
@@ -12,4 +12,4 @@ build:version:
         # Here docker buildx can use cached images created in previous step
         - docker buildx build --platform $DOCKER_PLATFORMS --file $DOCKERFILE --push --tag $CONTAINER_IMAGE .
     rules:
-        -   if: '$CI_COMMIT_REF_PROTECTED == "true"'
\ No newline at end of file
+        -   if: '$CI_COMMIT_REF_PROTECTED == "true" || $CI_COMMIT_BRANCH == "test"'
\ No newline at end of file
diff --git a/ExerciseChecker/.gitlab-ci/06_stageClean.yml b/ExerciseChecker/.gitlab-ci/07_stageClean.yml
similarity index 100%
rename from ExerciseChecker/.gitlab-ci/06_stageClean.yml
rename to ExerciseChecker/.gitlab-ci/07_stageClean.yml
diff --git a/ExerciseChecker/.gitlab-ci/07_stageUpload.yml b/ExerciseChecker/.gitlab-ci/08_stageUpload.yml
similarity index 100%
rename from ExerciseChecker/.gitlab-ci/07_stageUpload.yml
rename to ExerciseChecker/.gitlab-ci/08_stageUpload.yml
diff --git a/ExerciseChecker/.gitlab-ci/08_stageRelease.yml b/ExerciseChecker/.gitlab-ci/09_stageRelease.yml
similarity index 97%
rename from ExerciseChecker/.gitlab-ci/08_stageRelease.yml
rename to ExerciseChecker/.gitlab-ci/09_stageRelease.yml
index 325d173cd88c4336e573d1dd33f6ffbe7745c653..ad039e9aa41609db7d147f941fdf6bd73aa3a41b 100644
--- a/ExerciseChecker/.gitlab-ci/08_stageRelease.yml
+++ b/ExerciseChecker/.gitlab-ci/09_stageRelease.yml
@@ -12,7 +12,7 @@ release:dockerhub:latest:
         # Push latest tag
         - docker buildx imagetools create $CONTAINER_IMAGE --tag $DOCKER_REGISTRY_IMAGE:latest
     rules:
-        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+        -   if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test"'
 
 
 release:wiki:
diff --git a/ExerciseChecker/.idea/jetbrainsConfiguration b/ExerciseChecker/.idea/jetbrainsConfiguration
index 4d703a2dd39ec0c2b71bbbbda8900588c4e360bd..ffc5d65f9f0f0e825688177425e526131aa84631 160000
--- a/ExerciseChecker/.idea/jetbrainsConfiguration
+++ b/ExerciseChecker/.idea/jetbrainsConfiguration
@@ -1 +1 @@
-Subproject commit 4d703a2dd39ec0c2b71bbbbda8900588c4e360bd
+Subproject commit ffc5d65f9f0f0e825688177425e526131aa84631
diff --git a/ExerciseChecker/.idea/jsLinters/eslint.xml b/ExerciseChecker/.idea/jsLinters/eslint.xml
new file mode 100644
index 0000000000000000000000000000000000000000..541945bb0819b8ff4a3dae9431632ebd10e6f98b
--- /dev/null
+++ b/ExerciseChecker/.idea/jsLinters/eslint.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="EslintConfiguration">
+    <option name="fix-on-save" value="true" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/ExerciseChecker/package-lock.json b/ExerciseChecker/package-lock.json
index 9fbb82057e944f1175429e8f46a8abf09e272532..c92a13de29b79d66bcce864bba6765a5d2d7e166 100644
--- a/ExerciseChecker/package-lock.json
+++ b/ExerciseChecker/package-lock.json
@@ -1,44 +1,56 @@
 {
     "name": "dojo_exercise_checker",
-    "version": "2.2.0",
+    "version": "3.1.0",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "dojo_exercise_checker",
-            "version": "2.2.0",
+            "version": "3.1.0",
             "license": "AGPLv3",
             "dependencies": {
                 "ajv": "^8.12.0",
-                "axios": "^1.4.0",
+                "axios": "^1.6.2",
                 "boxen": "^5.1.2",
                 "chalk": "^4.1.2",
                 "dockerode": "^3.3.5",
                 "dotenv": "^16.3.1",
                 "dotenv-expand": "^10.0.0",
-                "fs-extra": "^11.1.1",
-                "http-status-codes": "^2.2.0",
+                "fs-extra": "^11.2.0",
+                "http-status-codes": "^2.3.0",
                 "json5": "^2.2.3",
                 "ora": "^5.4.1",
                 "tar-stream": "^3.1.6",
-                "winston": "^3.10.0",
-                "yaml": "^2.3.2"
+                "winston": "^3.11.0",
+                "yaml": "^2.3.4"
             },
             "bin": {
                 "dirmanager": "dist/app.js"
             },
             "devDependencies": {
-                "@types/dockerode": "^3.3.19",
-                "@types/fs-extra": "^11.0.1",
-                "@types/js-yaml": "^4.0.5",
-                "@types/node": "^18.17.1",
-                "@types/tar-stream": "^2.2.2",
+                "@types/dockerode": "^3.3.23",
+                "@types/fs-extra": "^11.0.4",
+                "@types/js-yaml": "^4.0.9",
+                "@types/node": "^18.19.2",
+                "@types/tar-stream": "^3.1.3",
+                "@typescript-eslint/eslint-plugin": "^6.13.2",
+                "@typescript-eslint/parser": "^6.13.2",
                 "dotenv-vault": "^1.25.0",
                 "genversion": "^3.1.1",
                 "pkg": "^5.8.1",
                 "tiny-typed-emitter": "^2.1.0",
                 "ts-node": "^10.9.1",
-                "typescript": "^5.1.6"
+                "typescript": "^5.3.2"
+            }
+        },
+        "node_modules/@aashutoshrathi/word-wrap": {
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+            "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=0.10.0"
             }
         },
         "node_modules/@babel/generator": {
@@ -144,6 +156,157 @@
                 "kuler": "^2.0.0"
             }
         },
+        "node_modules/@eslint-community/eslint-utils": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+            "dev": true,
+            "dependencies": {
+                "eslint-visitor-keys": "^3.3.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "peerDependencies": {
+                "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+            }
+        },
+        "node_modules/@eslint-community/regexpp": {
+            "version": "4.10.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+            "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+            "dev": true,
+            "engines": {
+                "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@eslint/eslintrc": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
+            "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.6.0",
+                "globals": "^13.19.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": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/@eslint/eslintrc/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,
+            "peer": true,
+            "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/@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,
+            "peer": true
+        },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "argparse": "^2.0.1"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/@eslint/eslintrc/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,
+            "peer": true
+        },
+        "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/@eslint/js": {
+            "version": "8.53.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
+            "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@humanwhocodes/config-array": {
+            "version": "0.11.13",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+            "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@humanwhocodes/object-schema": "^2.0.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.5"
+            },
+            "engines": {
+                "node": ">=10.10.0"
+            }
+        },
+        "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,
+            "peer": true,
+            "engines": {
+                "node": ">=12.22"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/nzakas"
+            }
+        },
+        "node_modules/@humanwhocodes/object-schema": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+            "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/@jridgewell/gen-mapping": {
             "version": "0.3.3",
             "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -680,9 +843,9 @@
             }
         },
         "node_modules/@types/dockerode": {
-            "version": "3.3.20",
-            "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.20.tgz",
-            "integrity": "sha512-Q+1e3z6SPWXR/Sk+WIyJFVsSDg78S7MDaGcwAh1WKlveO1tVO8TF1rOzJir5GLnqzEdUbclFKlw/4rhwESxwPw==",
+            "version": "3.3.23",
+            "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.23.tgz",
+            "integrity": "sha512-Lz5J+NFgZS4cEVhquwjIGH4oQwlVn2h7LXD3boitujBnzOE5o7s9H8hchEjoDK2SlRsJTogdKnQeiJgPPKLIEw==",
             "dev": true,
             "dependencies": {
                 "@types/docker-modem": "*",
@@ -690,9 +853,9 @@
             }
         },
         "node_modules/@types/fs-extra": {
-            "version": "11.0.2",
-            "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz",
-            "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==",
+            "version": "11.0.4",
+            "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
+            "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==",
             "dev": true,
             "dependencies": {
                 "@types/jsonfile": "*",
@@ -700,9 +863,15 @@
             }
         },
         "node_modules/@types/js-yaml": {
-            "version": "4.0.6",
-            "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.6.tgz",
-            "integrity": "sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw==",
+            "version": "4.0.9",
+            "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+            "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
+            "dev": true
+        },
+        "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
         },
         "node_modules/@types/jsonfile": {
@@ -715,9 +884,18 @@
             }
         },
         "node_modules/@types/node": {
-            "version": "18.18.3",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz",
-            "integrity": "sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==",
+            "version": "18.19.2",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.2.tgz",
+            "integrity": "sha512-6wzfBdbWpe8QykUkXBjtmO3zITA0A3FIjoy+in0Y2K4KrCiRhNYJIdwAPDffZ3G6GnaKaSLSEa9ZuORLfEoiwg==",
+            "dev": true,
+            "dependencies": {
+                "undici-types": "~5.26.4"
+            }
+        },
+        "node_modules/@types/semver": {
+            "version": "7.5.6",
+            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
+            "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
             "dev": true
         },
         "node_modules/@types/ssh2": {
@@ -730,9 +908,9 @@
             }
         },
         "node_modules/@types/tar-stream": {
-            "version": "2.2.3",
-            "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-2.2.3.tgz",
-            "integrity": "sha512-if3mugZfjVkXOMZdFjIHySxY13r6GXPpyOlsDmLffvyI7tLz9wXE8BFjNivXsvUeyJ1KNlOpfLnag+ISmxgxPw==",
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-3.1.3.tgz",
+            "integrity": "sha512-Zbnx4wpkWBMBSu5CytMbrT5ZpMiF55qgM+EpHzR4yIDu7mv52cej8hTkOc6K+LzpkOAbxwn/m7j3iO+/l42YkQ==",
             "dev": true,
             "dependencies": {
                 "@types/node": "*"
@@ -743,6 +921,202 @@
             "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz",
             "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g=="
         },
+        "node_modules/@typescript-eslint/eslint-plugin": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
+            "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
+            "dev": true,
+            "dependencies": {
+                "@eslint-community/regexpp": "^4.5.1",
+                "@typescript-eslint/scope-manager": "6.13.2",
+                "@typescript-eslint/type-utils": "6.13.2",
+                "@typescript-eslint/utils": "6.13.2",
+                "@typescript-eslint/visitor-keys": "6.13.2",
+                "debug": "^4.3.4",
+                "graphemer": "^1.4.0",
+                "ignore": "^5.2.4",
+                "natural-compare": "^1.4.0",
+                "semver": "^7.5.4",
+                "ts-api-utils": "^1.0.1"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+                "eslint": "^7.0.0 || ^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/parser": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
+            "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/scope-manager": "6.13.2",
+                "@typescript-eslint/types": "6.13.2",
+                "@typescript-eslint/typescript-estree": "6.13.2",
+                "@typescript-eslint/visitor-keys": "6.13.2",
+                "debug": "^4.3.4"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^7.0.0 || ^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/scope-manager": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
+            "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "6.13.2",
+                "@typescript-eslint/visitor-keys": "6.13.2"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/@typescript-eslint/type-utils": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
+            "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/typescript-estree": "6.13.2",
+                "@typescript-eslint/utils": "6.13.2",
+                "debug": "^4.3.4",
+                "ts-api-utils": "^1.0.1"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^7.0.0 || ^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/types": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
+            "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
+            "dev": true,
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/@typescript-eslint/typescript-estree": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
+            "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "6.13.2",
+                "@typescript-eslint/visitor-keys": "6.13.2",
+                "debug": "^4.3.4",
+                "globby": "^11.1.0",
+                "is-glob": "^4.0.3",
+                "semver": "^7.5.4",
+                "ts-api-utils": "^1.0.1"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/utils": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
+            "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
+            "dev": true,
+            "dependencies": {
+                "@eslint-community/eslint-utils": "^4.4.0",
+                "@types/json-schema": "^7.0.12",
+                "@types/semver": "^7.5.0",
+                "@typescript-eslint/scope-manager": "6.13.2",
+                "@typescript-eslint/types": "6.13.2",
+                "@typescript-eslint/typescript-estree": "6.13.2",
+                "semver": "^7.5.4"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^7.0.0 || ^8.0.0"
+            }
+        },
+        "node_modules/@typescript-eslint/visitor-keys": {
+            "version": "6.13.2",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
+            "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "6.13.2",
+                "eslint-visitor-keys": "^3.4.1"
+            },
+            "engines": {
+                "node": "^16.0.0 || >=18.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/@ungap/structured-clone": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+            "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/acorn": {
             "version": "8.10.0",
             "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
@@ -755,6 +1129,16 @@
                 "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,
+            "peer": true,
+            "peerDependencies": {
+                "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            }
+        },
         "node_modules/acorn-walk": {
             "version": "8.2.0",
             "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
@@ -915,9 +1299,9 @@
             }
         },
         "node_modules/axios": {
-            "version": "1.5.1",
-            "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
-            "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
+            "version": "1.6.2",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
+            "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
             "dependencies": {
                 "follow-redirects": "^1.15.0",
                 "form-data": "^4.0.0",
@@ -1056,6 +1440,16 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
         "node_modules/camelcase": {
             "version": "6.3.0",
             "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
@@ -1365,6 +1759,13 @@
                 "node": ">=4.0.0"
             }
         },
+        "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,
+            "peer": true
+        },
         "node_modules/defaults": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -1441,6 +1842,19 @@
                 "node": ">= 8.0"
             }
         },
+        "node_modules/doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "esutils": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
         "node_modules/dotenv": {
             "version": "16.3.1",
             "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
@@ -1543,16 +1957,176 @@
                 "node": ">=6"
             }
         },
-        "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==",
+        "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,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/eslint": {
+            "version": "8.53.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
+            "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@eslint-community/eslint-utils": "^4.2.0",
+                "@eslint-community/regexpp": "^4.6.1",
+                "@eslint/eslintrc": "^2.1.3",
+                "@eslint/js": "8.53.0",
+                "@humanwhocodes/config-array": "^0.11.13",
+                "@humanwhocodes/module-importer": "^1.0.1",
+                "@nodelib/fs.walk": "^1.2.8",
+                "@ungap/structured-clone": "^1.2.0",
+                "ajv": "^6.12.4",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.3.2",
+                "doctrine": "^3.0.0",
+                "escape-string-regexp": "^4.0.0",
+                "eslint-scope": "^7.2.2",
+                "eslint-visitor-keys": "^3.4.3",
+                "espree": "^9.6.1",
+                "esquery": "^1.4.2",
+                "esutils": "^2.0.2",
+                "fast-deep-equal": "^3.1.3",
+                "file-entry-cache": "^6.0.1",
+                "find-up": "^5.0.0",
+                "glob-parent": "^6.0.2",
+                "globals": "^13.19.0",
+                "graphemer": "^1.4.0",
+                "ignore": "^5.2.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "is-path-inside": "^3.0.3",
+                "js-yaml": "^4.1.0",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash.merge": "^4.6.2",
+                "minimatch": "^3.1.2",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.3",
+                "strip-ansi": "^6.0.1",
+                "text-table": "^0.2.0"
+            },
+            "bin": {
+                "eslint": "bin/eslint.js"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/eslint-scope": {
+            "version": "7.2.2",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+            "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "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,
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/eslint/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,
+            "peer": true,
+            "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/eslint/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,
+            "peer": true
+        },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "is-glob": "^4.0.3"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/eslint/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,
+            "peer": true,
+            "dependencies": {
+                "argparse": "^2.0.1"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/eslint/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,
+            "peer": true
+        },
+        "node_modules/espree": {
+            "version": "9.6.1",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+            "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
             "dev": true,
+            "peer": true,
+            "dependencies": {
+                "acorn": "^8.9.0",
+                "acorn-jsx": "^5.3.2",
+                "eslint-visitor-keys": "^3.4.1"
+            },
             "engines": {
-                "node": ">=10"
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "url": "https://opencollective.com/eslint"
             }
         },
         "node_modules/esprima": {
@@ -1568,6 +2142,52 @@
                 "node": ">=4"
             }
         },
+        "node_modules/esquery": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+            "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+            "dev": true,
+            "peer": true,
+            "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,
+            "peer": true,
+            "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,
+            "peer": true,
+            "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,
+            "peer": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/expand-template": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
@@ -1617,6 +2237,13 @@
                 "node": ">=8.6.0"
             }
         },
+        "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,
+            "peer": true
+        },
         "node_modules/fast-levenshtein": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
@@ -1673,6 +2300,19 @@
                 "node": ">=0.8.0"
             }
         },
+        "node_modules/file-entry-cache": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "flat-cache": "^3.0.4"
+            },
+            "engines": {
+                "node": "^10.12.0 || >=12.0.0"
+            }
+        },
         "node_modules/filelist": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
@@ -1733,6 +2373,45 @@
                 "parents": "^1.0.1"
             }
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "locate-path": "^6.0.0",
+                "path-exists": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/flat-cache": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz",
+            "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "flatted": "^3.2.9",
+                "keyv": "^4.5.3",
+                "rimraf": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=12.0.0"
+            }
+        },
+        "node_modules/flatted": {
+            "version": "3.2.9",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
+            "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/fn.name": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
@@ -1816,9 +2495,9 @@
             "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
         },
         "node_modules/fs-extra": {
-            "version": "11.1.1",
-            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
-            "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==",
+            "version": "11.2.0",
+            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+            "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
             "dependencies": {
                 "graceful-fs": "^4.2.0",
                 "jsonfile": "^6.0.1",
@@ -1828,6 +2507,13 @@
                 "node": ">=14.14"
             }
         },
+        "node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/genversion": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.1.1.tgz",
@@ -1868,6 +2554,27 @@
             "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
             "dev": true
         },
+        "node_modules/glob": {
+            "version": "7.2.3",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.1.1",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
         "node_modules/glob-parent": {
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -1880,6 +2587,22 @@
                 "node": ">= 6"
             }
         },
+        "node_modules/globals": {
+            "version": "13.23.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
+            "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "type-fest": "^0.20.2"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
         "node_modules/globby": {
             "version": "11.1.0",
             "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -1905,6 +2628,12 @@
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
             "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
         },
+        "node_modules/graphemer": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+            "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+            "dev": true
+        },
         "node_modules/has": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
@@ -2006,6 +2735,33 @@
                 "node": ">= 4"
             }
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=0.8.19"
+            }
+        },
         "node_modules/indent-string": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -2015,6 +2771,17 @@
                 "node": ">=8"
             }
         },
+        "node_modules/inflight": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
         "node_modules/inherits": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -2161,6 +2928,16 @@
                 "node": ">=0.12.0"
             }
         },
+        "node_modules/is-path-inside": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+            "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/is-retry-allowed": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
@@ -2259,6 +3036,13 @@
                 "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,
+            "peer": true
+        },
         "node_modules/json-parse-better-errors": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -2270,6 +3054,13 @@
             "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
             "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
         },
+        "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,
+            "peer": true
+        },
         "node_modules/json5": {
             "version": "2.2.3",
             "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -2292,11 +3083,51 @@
                 "graceful-fs": "^4.1.6"
             }
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "json-buffer": "3.0.1"
+            }
+        },
         "node_modules/kuler": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
             "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "prelude-ls": "^1.2.1",
+                "type-check": "~0.4.0"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "p-locate": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
         "node_modules/lodash": {
             "version": "4.17.21",
             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -2309,6 +3140,13 @@
             "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==",
             "dev": true
         },
+        "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,
+            "peer": true
+        },
         "node_modules/lodash.template": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
@@ -2526,6 +3364,12 @@
             "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
             "dev": true
         },
+        "node_modules/natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+            "dev": true
+        },
         "node_modules/natural-orderby": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz",
@@ -2606,6 +3450,31 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
+        "node_modules/optionator": {
+            "version": "0.9.3",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+            "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@aashutoshrathi/word-wrap": "^1.2.3",
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/optionator/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,
+            "peer": true
+        },
         "node_modules/ora": {
             "version": "5.4.1",
             "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
@@ -2646,6 +3515,51 @@
                 "node": ">=8"
             }
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "yocto-queue": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "p-limit": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "callsites": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
         "node_modules/parents": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@@ -2678,6 +3592,26 @@
                 "cross-spawn": "^7.0.3"
             }
         },
+        "node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/path-key": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -2859,6 +3793,16 @@
                 "node": ">=10"
             }
         },
+        "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,
+            "peer": true,
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
         "node_modules/process-nextick-args": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -2992,6 +3936,16 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
+        "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,
+            "peer": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
         "node_modules/resolve/node_modules/is-core-module": {
             "version": "2.13.0",
             "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
@@ -3026,6 +3980,22 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/rimraf": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
         "node_modules/run-async": {
             "version": "2.4.1",
             "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -3426,6 +4396,13 @@
             "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
             "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
         },
+        "node_modules/text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/through": {
             "version": "2.3.8",
             "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -3485,6 +4462,18 @@
                 "node": ">= 14.0.0"
             }
         },
+        "node_modules/ts-api-utils": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
+            "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+            "dev": true,
+            "engines": {
+                "node": ">=16.13.0"
+            },
+            "peerDependencies": {
+                "typescript": ">=4.2.0"
+            }
+        },
         "node_modules/ts-node": {
             "version": "10.9.1",
             "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@@ -3551,6 +4540,19 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
         },
+        "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,
+            "peer": true,
+            "dependencies": {
+                "prelude-ls": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
         "node_modules/type-fest": {
             "version": "0.20.2",
             "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -3563,9 +4565,9 @@
             }
         },
         "node_modules/typescript": {
-            "version": "5.2.2",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
-            "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+            "version": "5.3.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
+            "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
             "dev": true,
             "bin": {
                 "tsc": "bin/tsc",
@@ -3575,6 +4577,12 @@
                 "node": ">=14.17"
             }
         },
+        "node_modules/undici-types": {
+            "version": "5.26.5",
+            "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+            "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+            "dev": true
+        },
         "node_modules/universalify": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -3653,11 +4661,11 @@
             }
         },
         "node_modules/winston": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/winston/-/winston-3.10.0.tgz",
-            "integrity": "sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==",
+            "version": "3.11.0",
+            "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
+            "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==",
             "dependencies": {
-                "@colors/colors": "1.5.0",
+                "@colors/colors": "^1.6.0",
                 "@dabh/diagnostics": "^2.0.2",
                 "async": "^3.2.3",
                 "is-stream": "^2.0.0",
@@ -3686,6 +4694,14 @@
                 "node": ">= 6.4.0"
             }
         },
+        "node_modules/winston/node_modules/@colors/colors": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+            "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+            "engines": {
+                "node": ">=0.1.90"
+            }
+        },
         "node_modules/wordwrap": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -3729,9 +4745,9 @@
             "dev": true
         },
         "node_modules/yaml": {
-            "version": "2.3.2",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
-            "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
+            "version": "2.3.4",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
+            "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
             "engines": {
                 "node": ">= 14"
             }
@@ -3771,6 +4787,19 @@
             "engines": {
                 "node": ">=6"
             }
+        },
+        "node_modules/yocto-queue": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+            "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
         }
     }
 }
diff --git a/ExerciseChecker/package.json b/ExerciseChecker/package.json
index 49ac5b240792a025968980b4aeb5255c41da5b26..2825a8bc29730c3cd5f84c1fdc5e24450b459dfd 100644
--- a/ExerciseChecker/package.json
+++ b/ExerciseChecker/package.json
@@ -1,7 +1,7 @@
 {
     "name"           : "dojo_exercise_checker",
     "description"    : "App that check an exercise of the Dojo project",
-    "version"        : "3.0.1",
+    "version"        : "3.1.0",
     "license"        : "AGPLv3",
     "author"         : "Michaël Minelli <dojo@minelli.me>",
     "main"           : "dist/app.js",
@@ -22,38 +22,41 @@
     },
     "scripts"        : {
         "dotenv:build": "npx dotenv-vault local build",
+        "lint"        : "npx eslint .",
         "genversion"  : "npx genversion -s -e src/config/Version.ts",
         "build"       : "npm run genversion; npx tsc",
-        "start:dev"   : "npm run genversion; npx ts-node src/app.ts",
+        "start:dev"   : "npm run genversion; npm run lint; npx ts-node src/app.ts",
         "test"        : "echo \"Error: no test specified\" && exit 1"
     },
     "dependencies"   : {
         "ajv"              : "^8.12.0",
-        "axios"            : "^1.4.0",
+        "axios"            : "^1.6.2",
         "boxen"            : "^5.1.2",
         "chalk"            : "^4.1.2",
         "dockerode"        : "^3.3.5",
         "dotenv"           : "^16.3.1",
         "dotenv-expand"    : "^10.0.0",
-        "fs-extra"         : "^11.1.1",
-        "http-status-codes": "^2.2.0",
+        "fs-extra"         : "^11.2.0",
+        "http-status-codes": "^2.3.0",
         "json5"            : "^2.2.3",
         "ora"              : "^5.4.1",
         "tar-stream"       : "^3.1.6",
-        "winston"          : "^3.10.0",
-        "yaml"             : "^2.3.2"
+        "winston"          : "^3.11.0",
+        "yaml"             : "^2.3.4"
     },
     "devDependencies": {
-        "@types/dockerode"  : "^3.3.19",
-        "@types/fs-extra"   : "^11.0.1",
-        "@types/js-yaml"    : "^4.0.5",
-        "@types/node"       : "^18.17.1",
-        "@types/tar-stream" : "^2.2.2",
-        "dotenv-vault"      : "^1.25.0",
-        "genversion"        : "^3.1.1",
-        "pkg"               : "^5.8.1",
-        "tiny-typed-emitter": "^2.1.0",
-        "ts-node"           : "^10.9.1",
-        "typescript"        : "^5.1.6"
+        "@types/dockerode"                : "^3.3.23",
+        "@types/fs-extra"                 : "^11.0.4",
+        "@types/js-yaml"                  : "^4.0.9",
+        "@types/node"                     : "^18.19.2",
+        "@types/tar-stream"               : "^3.1.3",
+        "@typescript-eslint/eslint-plugin": "^6.13.2",
+        "@typescript-eslint/parser"       : "^6.13.2",
+        "dotenv-vault"                    : "^1.25.0",
+        "genversion"                      : "^3.1.1",
+        "pkg"                             : "^5.8.1",
+        "tiny-typed-emitter"              : "^2.1.0",
+        "ts-node"                         : "^10.9.1",
+        "typescript"                      : "^5.3.2"
     }
 }
diff --git a/ExerciseChecker/src/app.ts b/ExerciseChecker/src/app.ts
index 63eef070ae3635dcf2c0245dd9e39337469df870..79e07d2e8e23ef4900d9a228f8a5e31767d8d66c 100644
--- a/ExerciseChecker/src/app.ts
+++ b/ExerciseChecker/src/app.ts
@@ -1,21 +1,24 @@
 // Read from the .env file
-// ATTENTION : This lines MUST be the first of this file (except for the path import)
-const path = require('node:path');
-const myEnv = require('dotenv').config({
-                                           path      : path.join(__dirname, '../.env'),
-                                           DOTENV_KEY: 'dotenv://:key_bebfddf18e3dd9a0bafafe0e383313f75add1da6fbe41ea5fde51f37ef1776aa@dotenv.local/vault/.env.vault?environment=development'
-                                       });
-require('dotenv-expand').expand(myEnv);
+// ATTENTION : These lines MUST be the first of this file (except for the path import)
+import path = require('node:path');
+import myEnv = require('dotenv');
+import dotenvExpand = require('dotenv-expand');
+
+
+dotenvExpand.expand(myEnv.config({
+                                     path      : path.join(__dirname, '../.env'),
+                                     DOTENV_KEY: 'dotenv://:key_bebfddf18e3dd9a0bafafe0e383313f75add1da6fbe41ea5fde51f37ef1776aa@dotenv.local/vault/.env.vault?environment=development'
+                                 }));
+
 require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be the second of this file
 
+
 import ClientsSharedConfig                  from './sharedByClients/config/ClientsSharedConfig';
 import Styles                               from './types/Style';
 import Icon                                 from './sharedByClients/types/Icon';
 import RecursiveFilesStats                  from './shared/helpers/recursiveFilesStats/RecursiveFilesStats';
 import Toolbox                              from './shared/helpers/Toolbox';
 import ExerciseCheckerError                 from './shared/types/Dojo/ExerciseCheckerError';
-import { exec }                             from 'child_process';
-import util                                 from 'util';
 import fs                                   from 'fs-extra';
 import HttpManager                          from './managers/HttpManager';
 import DojoBackendManager                   from './managers/DojoBackendManager';
@@ -28,8 +31,6 @@ import ClientsSharedExerciseHelper          from './sharedByClients/helpers/Dojo
 
 
 (async () => {
-    const execAsync = util.promisify(exec);
-
     HttpManager.registerAxiosInterceptor();
 
     console.log(Styles.APP_NAME(`${ Config.appName } (version {{VERSION}})`));
@@ -94,13 +95,13 @@ import ClientsSharedExerciseHelper          from './sharedByClients/helpers/Dojo
                     }
                 });
 
-                exerciseDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
+                exerciseDockerCompose.events.on('finished', (success: boolean) => {
                     success ? resolve() : reject();
                 });
 
                 exerciseDockerCompose.run();
             });
-        } catch ( error ) { }
+        } catch ( error ) { /* empty */ }
 
         fs.rmSync(composeOverridePath, { force: true });
         fs.writeFileSync(path.join(Config.folders.resultsDojo, 'dockerComposeLogs.txt'), exerciseDockerCompose.allLogs);
@@ -138,7 +139,7 @@ import ClientsSharedExerciseHelper          from './sharedByClients/helpers/Dojo
 
                 exerciseResultsValidation.run();
             });
-        } catch ( error ) { }
+        } catch ( error ) { /* empty */ }
     }
 
 
@@ -146,9 +147,9 @@ import ClientsSharedExerciseHelper          from './sharedByClients/helpers/Dojo
     {
         try {
             console.log(Styles.INFO(`${ Icon.INFO } Uploading results to the dojo server`));
-            const commit: any = {};
+            const commit: Record<string, string> = {};
             Toolbox.getKeysWithPrefix(process.env, 'CI_COMMIT_').forEach(key => {
-                commit[Toolbox.snakeToCamel(key.replace('CI_COMMIT_', ''))] = process.env[key];
+                commit[Toolbox.snakeToCamel(key.replace('CI_COMMIT_', ''))] = process.env[key] as string;
             });
 
             const files = await RecursiveFilesStats.explore(Config.folders.resultsVolume, {
diff --git a/ExerciseChecker/src/managers/DojoBackendManager.ts b/ExerciseChecker/src/managers/DojoBackendManager.ts
index 0799a3c8fe09cc874939d93818f345fe3779e1fb..102069ead041b6877029dad772edb7fb81a45211 100644
--- a/ExerciseChecker/src/managers/DojoBackendManager.ts
+++ b/ExerciseChecker/src/managers/DojoBackendManager.ts
@@ -5,6 +5,7 @@ import ExerciseAssignment  from '../sharedByClients/models/ExerciseAssignment';
 import Config              from '../config/Config';
 import ExerciseResultsFile from '../shared/types/Dojo/ExerciseResultsFile';
 import ApiRoute            from '../sharedByClients/types/Dojo/ApiRoute';
+import { IFileDirStat }    from '../shared/helpers/recursiveFilesStats/RecursiveFilesStats';
 
 
 class DojoBackendManager {
@@ -20,18 +21,14 @@ class DojoBackendManager {
         }
     }
 
-    public async sendResults(exitCode: number, commit: any, results: ExerciseResultsFile, files: any, archiveBase64: string): Promise<void> {
-        try {
-            await axios.post(this.getApiUrl(ApiRoute.EXERCISE_RESULTS).replace('{{id}}', Config.exercise.id), {
-                exitCode     : exitCode,
-                commit       : JSON.stringify(commit),
-                results      : JSON.stringify(results),
-                files        : JSON.stringify(files),
-                archiveBase64: archiveBase64
-            });
-        } catch ( error ) {
-            throw error;
-        }
+    public async sendResults(exitCode: number, commit: Record<string, string>, results: ExerciseResultsFile, files: Array<IFileDirStat>, archiveBase64: string): Promise<void> {
+        await axios.post(this.getApiUrl(ApiRoute.EXERCISE_RESULTS).replace('{{id}}', Config.exercise.id), {
+            exitCode     : exitCode,
+            commit       : JSON.stringify(commit),
+            results      : JSON.stringify(results),
+            files        : JSON.stringify(files),
+            archiveBase64: archiveBase64
+        });
     }
 }
 
diff --git a/ExerciseChecker/src/managers/HttpManager.ts b/ExerciseChecker/src/managers/HttpManager.ts
index 54d6990ad2c31f3a0307a56b13f305b0285ee0db..4733b3369a9af02da3d3324ddfe96b6923b4a516 100644
--- a/ExerciseChecker/src/managers/HttpManager.ts
+++ b/ExerciseChecker/src/managers/HttpManager.ts
@@ -43,7 +43,7 @@ class HttpManager {
                     config.headers['Content-Type'] = 'multipart/form-data';
                 }
 
-                config.headers.Authorization = `ExerciseSecret ${ Config.exercise.secret }`;
+                config.headers.ExerciseSecret = Config.exercise.secret;
 
                 config.headers['client'] = 'DojoExerciseChecker';
                 config.headers['client-version'] = version;
@@ -59,7 +59,7 @@ class HttpManager {
         }, (error) => {
             if ( error.response ) {
                 if ( error.response.status === StatusCodes.METHOD_NOT_ALLOWED && error.response.data ) {
-                    const data: DojoBackendResponse<{}> = error.response.data;
+                    const data: DojoBackendResponse<void> = error.response.data;
 
                     switch ( data.code ) {
                         case DojoStatusCode.CLIENT_NOT_SUPPORTED:
diff --git a/ExerciseChecker/src/shared b/ExerciseChecker/src/shared
index 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989..101cc26895eb0b5fe97e03bb96039e0cddd94391 160000
--- a/ExerciseChecker/src/shared
+++ b/ExerciseChecker/src/shared
@@ -1 +1 @@
-Subproject commit 4a5eb68209ae9204b6d4cc8020bd62cf6a5be989
+Subproject commit 101cc26895eb0b5fe97e03bb96039e0cddd94391