Repository: docker-seminar/docker-tutorial
Pull request #52: infra(CI): Add GitHub Actions workflow for Vitest UI deployment to GitHub Pages
Author: login: kimseungbin
Created: 2025-04-15T05:03:38Z | Merged: 2025-04-16T05:30:32Z

- Close #49

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Chores**
  - Upgraded testing dependencies and added a new script for running tests with coverage and UI.
  - Introduced a new GitHub Actions workflow to automate deployment of the Vitest UI to GitHub Pages.
  - Enhanced test reporting for GitHub Actions by including an HTML reporter.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Linked issue #49: infra(CI): Investigate and configure Vitest GitHub reporter for workflow summary
## 💡 Details

The Vitest GitHub repoerter is currently not configured in the test command used within GitHub Actions.
This repoerter is designed to emit workflow commands that GitHub Actions can capture and display as part of the job summary.

Adding and validating this repoerter is expected to improve test visibility in CI.


## ✅ Todos

- [x] Add `--reporter=github` to the Vitest test command in the workflow

## References

- https://vitest.dev/guide/reporters

Diff:
diff --git a/.github/workflows/deploy-vitest-ui.yaml b/.github/workflows/deploy-vitest-ui.yaml
new file mode 100644
index 0000000..2c5bb16
--- /dev/null
+++ b/.github/workflows/deploy-vitest-ui.yaml
@@ -0,0 +1,46 @@
+name: Deploy Vitest UI to GitHub Pages
+
+on:
+  push:
+    branches:
+      - main
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+concurrency:
+  group: pages
+  cancel-in-progress: false
+
+jobs:
+  deploy:
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.outputs.page_url }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install dependencies
+        working-directory: server
+        run: npm install
+
+      - name: Build Vitest UI
+        working-directory: server
+        run: npm run test:preview
+
+      - name: Setup Pages
+        uses: actions/configure-pages@v5
+
+      - name: Upload artifact
+        uses: actions/upload-pages-artifact@v3
+        with:
+          path: server/html
+
+      - name: Deploy to GitHub Pages
+        id: deployment
+        uses: actions/deploy-pages@v4
\ No newline at end of file
diff --git a/server/infra/vite.config.ts b/server/infra/vite.config.ts
index 91117ad..891b2a7 100644
--- a/server/infra/vite.config.ts
+++ b/server/infra/vite.config.ts
@@ -1,13 +1,13 @@
 import { defineConfig } from 'vitest/config'
 import swc from 'unplugin-swc'
 
-const isGitHubActions = process.env.GITHUB_ACTIONS === 'true'
+const isGitHubActions = process.env.GITHUB_ACTIONS
 
 export default defineConfig({
 	test: {
 		include: ['**/*.spec.ts'],
 		globals: true,
-		reporters: isGitHubActions ? ['verbose', 'github-actions'] : ['verbose'],
+		reporters: isGitHubActions ? ['verbose', 'github-actions', 'html'] : ['verbose'],
 	},
 	plugins: [
 		swc.vite({
diff --git a/server/package-lock.json b/server/package-lock.json
index 6b9eb31..99f8ecd 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -29,7 +29,8 @@
         "@types/jest": "^29.5.14",
         "@types/node": "^22.10.7",
         "@types/supertest": "^6.0.2",
-        "@vitest/coverage-v8": "^3.0.9",
+        "@vitest/coverage-v8": "^3.1.1",
+        "@vitest/ui": "^3.1.1",
         "eslint": "^9.18.0",
         "eslint-config-prettier": "^10.0.1",
         "eslint-plugin-jsdoc": "^50.6.9",
@@ -46,7 +47,7 @@
         "typescript": "^5.7.3",
         "typescript-eslint": "^8.20.0",
         "unplugin-swc": "^1.5.1",
-        "vitest": "^3.0.9"
+        "vitest": "^3.1.1"
       }
     },
     "node_modules/@ampproject/remapping": {
@@ -769,9 +770,9 @@
       }
     },
     "node_modules/@esbuild/aix-ppc64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
-      "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
+      "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
       "cpu": [
         "ppc64"
       ],
@@ -786,9 +787,9 @@
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
-      "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
+      "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
       "cpu": [
         "arm"
       ],
@@ -803,9 +804,9 @@
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
-      "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
+      "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
       "cpu": [
         "arm64"
       ],
@@ -820,9 +821,9 @@
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
-      "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
+      "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
       "cpu": [
         "x64"
       ],
@@ -837,9 +838,9 @@
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
-      "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
+      "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
       "cpu": [
         "arm64"
       ],
@@ -854,9 +855,9 @@
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
-      "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
+      "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
       "cpu": [
         "x64"
       ],
@@ -871,9 +872,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
-      "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
+      "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
       "cpu": [
         "arm64"
       ],
@@ -888,9 +889,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
-      "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
+      "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
       "cpu": [
         "x64"
       ],
@@ -905,9 +906,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
-      "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
+      "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
       "cpu": [
         "arm"
       ],
@@ -922,9 +923,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
-      "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
+      "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
       "cpu": [
         "arm64"
       ],
@@ -939,9 +940,9 @@
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
-      "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
+      "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
       "cpu": [
         "ia32"
       ],
@@ -956,9 +957,9 @@
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
-      "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
+      "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
       "cpu": [
         "loong64"
       ],
@@ -973,9 +974,9 @@
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
-      "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
+      "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
       "cpu": [
         "mips64el"
       ],
@@ -990,9 +991,9 @@
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
-      "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
+      "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
       "cpu": [
         "ppc64"
       ],
@@ -1007,9 +1008,9 @@
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
-      "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
+      "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
       "cpu": [
         "riscv64"
       ],
@@ -1024,9 +1025,9 @@
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
-      "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
+      "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
       "cpu": [
         "s390x"
       ],
@@ -1041,9 +1042,9 @@
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
-      "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
+      "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
       "cpu": [
         "x64"
       ],
@@ -1058,9 +1059,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
-      "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
+      "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
       "cpu": [
         "arm64"
       ],
@@ -1075,9 +1076,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
-      "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
+      "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
       "cpu": [
         "x64"
       ],
@@ -1092,9 +1093,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
-      "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
+      "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
       "cpu": [
         "arm64"
       ],
@@ -1109,9 +1110,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
-      "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
+      "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
       "cpu": [
         "x64"
       ],
@@ -1126,9 +1127,9 @@
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
-      "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
+      "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
       "cpu": [
         "x64"
       ],
@@ -1143,9 +1144,9 @@
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
-      "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
+      "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
       "cpu": [
         "arm64"
       ],
@@ -1160,9 +1161,9 @@
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
-      "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
+      "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
       "cpu": [
         "ia32"
       ],
@@ -1177,9 +1178,9 @@
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
-      "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
+      "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
       "cpu": [
         "x64"
       ],
@@ -3086,6 +3087,13 @@
         "url": "https://opencollective.com/unts"
       }
     },
+    "node_modules/@polka/url": {
+      "version": "1.0.0-next.29",
+      "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+      "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@rollup/pluginutils": {
       "version": "5.1.4",
       "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
@@ -4293,9 +4301,9 @@
       }
     },
     "node_modules/@vitest/coverage-v8": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.9.tgz",
-      "integrity": "sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz",
+      "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4308,7 +4316,7 @@
         "istanbul-reports": "^3.1.7",
         "magic-string": "^0.30.17",
         "magicast": "^0.3.5",
-        "std-env": "^3.8.0",
+        "std-env": "^3.8.1",
         "test-exclude": "^7.0.1",
         "tinyrainbow": "^2.0.0"
       },
@@ -4316,8 +4324,8 @@
         "url": "https://opencollective.com/vitest"
       },
       "peerDependencies": {
-        "@vitest/browser": "3.0.9",
-        "vitest": "3.0.9"
+        "@vitest/browser": "3.1.1",
+        "vitest": "3.1.1"
       },
       "peerDependenciesMeta": {
         "@vitest/browser": {
@@ -4453,14 +4461,14 @@
       }
     },
     "node_modules/@vitest/expect": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz",
-      "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz",
+      "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/spy": "3.0.9",
-        "@vitest/utils": "3.0.9",
+        "@vitest/spy": "3.1.1",
+        "@vitest/utils": "3.1.1",
         "chai": "^5.2.0",
         "tinyrainbow": "^2.0.0"
       },
@@ -4469,13 +4477,13 @@
       }
     },
     "node_modules/@vitest/mocker": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz",
-      "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz",
+      "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/spy": "3.0.9",
+        "@vitest/spy": "3.1.1",
         "estree-walker": "^3.0.3",
         "magic-string": "^0.30.17"
       },
@@ -4496,9 +4504,9 @@
       }
     },
     "node_modules/@vitest/pretty-format": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz",
-      "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz",
+      "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4509,13 +4517,13 @@
       }
     },
     "node_modules/@vitest/runner": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz",
-      "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz",
+      "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/utils": "3.0.9",
+        "@vitest/utils": "3.1.1",
         "pathe": "^2.0.3"
       },
       "funding": {
@@ -4523,13 +4531,13 @@
       }
     },
     "node_modules/@vitest/snapshot": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz",
-      "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz",
+      "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/pretty-format": "3.0.9",
+        "@vitest/pretty-format": "3.1.1",
         "magic-string": "^0.30.17",
         "pathe": "^2.0.3"
       },
@@ -4538,9 +4546,9 @@
       }
     },
     "node_modules/@vitest/spy": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz",
-      "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz",
+      "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4550,14 +4558,36 @@
         "url": "https://opencollective.com/vitest"
       }
     },
+    "node_modules/@vitest/ui": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.1.1.tgz",
+      "integrity": "sha512-2HpiRIYg3dlvAJBV9RtsVswFgUSJK4Sv7QhpxoP0eBGkYwzGIKP34PjaV00AULQi9Ovl6LGyZfsetxDWY5BQdQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "3.1.1",
+        "fflate": "^0.8.2",
+        "flatted": "^3.3.3",
+        "pathe": "^2.0.3",
+        "sirv": "^3.0.1",
+        "tinyglobby": "^0.2.12",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "vitest": "3.1.1"
+      }
+    },
     "node_modules/@vitest/utils": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz",
-      "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz",
+      "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/pretty-format": "3.0.9",
+        "@vitest/pretty-format": "3.1.1",
         "loupe": "^3.1.3",
         "tinyrainbow": "^2.0.0"
       },
@@ -6591,9 +6621,9 @@
       }
     },
     "node_modules/esbuild": {
-      "version": "0.25.1",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
-      "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
+      "version": "0.25.2",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
+      "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
       "dev": true,
       "hasInstallScript": true,
       "license": "MIT",
@@ -6604,31 +6634,31 @@
         "node": ">=18"
       },
       "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.25.1",
-        "@esbuild/android-arm": "0.25.1",
-        "@esbuild/android-arm64": "0.25.1",
-        "@esbuild/android-x64": "0.25.1",
-        "@esbuild/darwin-arm64": "0.25.1",
-        "@esbuild/darwin-x64": "0.25.1",
-        "@esbuild/freebsd-arm64": "0.25.1",
-        "@esbuild/freebsd-x64": "0.25.1",
-        "@esbuild/linux-arm": "0.25.1",
-        "@esbuild/linux-arm64": "0.25.1",
-        "@esbuild/linux-ia32": "0.25.1",
-        "@esbuild/linux-loong64": "0.25.1",
-        "@esbuild/linux-mips64el": "0.25.1",
-        "@esbuild/linux-ppc64": "0.25.1",
-        "@esbuild/linux-riscv64": "0.25.1",
-        "@esbuild/linux-s390x": "0.25.1",
-        "@esbuild/linux-x64": "0.25.1",
-        "@esbuild/netbsd-arm64": "0.25.1",
-        "@esbuild/netbsd-x64": "0.25.1",
-        "@esbuild/openbsd-arm64": "0.25.1",
-        "@esbuild/openbsd-x64": "0.25.1",
-        "@esbuild/sunos-x64": "0.25.1",
-        "@esbuild/win32-arm64": "0.25.1",
-        "@esbuild/win32-ia32": "0.25.1",
-        "@esbuild/win32-x64": "0.25.1"
+        "@esbuild/aix-ppc64": "0.25.2",
+        "@esbuild/android-arm": "0.25.2",
+        "@esbuild/android-arm64": "0.25.2",
+        "@esbuild/android-x64": "0.25.2",
+        "@esbuild/darwin-arm64": "0.25.2",
+        "@esbuild/darwin-x64": "0.25.2",
+        "@esbuild/freebsd-arm64": "0.25.2",
+        "@esbuild/freebsd-x64": "0.25.2",
+        "@esbuild/linux-arm": "0.25.2",
+        "@esbuild/linux-arm64": "0.25.2",
+        "@esbuild/linux-ia32": "0.25.2",
+        "@esbuild/linux-loong64": "0.25.2",
+        "@esbuild/linux-mips64el": "0.25.2",
+        "@esbuild/linux-ppc64": "0.25.2",
+        "@esbuild/linux-riscv64": "0.25.2",
+        "@esbuild/linux-s390x": "0.25.2",
+        "@esbuild/linux-x64": "0.25.2",
+        "@esbuild/netbsd-arm64": "0.25.2",
+        "@esbuild/netbsd-x64": "0.25.2",
+        "@esbuild/openbsd-arm64": "0.25.2",
+        "@esbuild/openbsd-x64": "0.25.2",
+        "@esbuild/sunos-x64": "0.25.2",
+        "@esbuild/win32-arm64": "0.25.2",
+        "@esbuild/win32-ia32": "0.25.2",
+        "@esbuild/win32-x64": "0.25.2"
       }
     },
     "node_modules/escalade": {
@@ -7236,6 +7266,28 @@
         "bser": "2.1.1"
       }
     },
+    "node_modules/fdir": {
+      "version": "6.4.3",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
+      "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fflate": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+      "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/file-entry-cache": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -9614,6 +9666,16 @@
         "mkdirp": "bin/cmd.js"
       }
     },
+    "node_modules/mrmime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+      "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -11131,6 +11193,21 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/sirv": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz",
+      "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@polka/url": "^1.0.0-next.24",
+        "mrmime": "^2.0.0",
+        "totalist": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/sisteransi": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -11907,6 +11984,23 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/tinyglobby": {
+      "version": "0.2.12",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
+      "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.4.3",
+        "picomatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
     "node_modules/tinypool": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
@@ -11997,6 +12091,16 @@
         "url": "https://github.com/sponsors/Borewit"
       }
     },
+    "node_modules/totalist": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+      "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/tree-kill": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -12453,9 +12557,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
-      "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
+      "version": "6.2.6",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
+      "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -12525,9 +12629,9 @@
       }
     },
     "node_modules/vite-node": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz",
-      "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz",
+      "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -12548,31 +12652,31 @@
       }
     },
     "node_modules/vitest": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz",
-      "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz",
+      "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@vitest/expect": "3.0.9",
-        "@vitest/mocker": "3.0.9",
-        "@vitest/pretty-format": "^3.0.9",
-        "@vitest/runner": "3.0.9",
-        "@vitest/snapshot": "3.0.9",
-        "@vitest/spy": "3.0.9",
-        "@vitest/utils": "3.0.9",
+        "@vitest/expect": "3.1.1",
+        "@vitest/mocker": "3.1.1",
+        "@vitest/pretty-format": "^3.1.1",
+        "@vitest/runner": "3.1.1",
+        "@vitest/snapshot": "3.1.1",
+        "@vitest/spy": "3.1.1",
+        "@vitest/utils": "3.1.1",
         "chai": "^5.2.0",
         "debug": "^4.4.0",
-        "expect-type": "^1.1.0",
+        "expect-type": "^1.2.0",
         "magic-string": "^0.30.17",
         "pathe": "^2.0.3",
-        "std-env": "^3.8.0",
+        "std-env": "^3.8.1",
         "tinybench": "^2.9.0",
         "tinyexec": "^0.3.2",
         "tinypool": "^1.0.2",
         "tinyrainbow": "^2.0.0",
         "vite": "^5.0.0 || ^6.0.0",
-        "vite-node": "3.0.9",
+        "vite-node": "3.1.1",
         "why-is-node-running": "^2.3.0"
       },
       "bin": {
@@ -12588,8 +12692,8 @@
         "@edge-runtime/vm": "*",
         "@types/debug": "^4.1.12",
         "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
-        "@vitest/browser": "3.0.9",
-        "@vitest/ui": "3.0.9",
+        "@vitest/browser": "3.1.1",
+        "@vitest/ui": "3.1.1",
         "happy-dom": "*",
         "jsdom": "*"
       },
diff --git a/server/package.json b/server/package.json
index 3b31818..c17b74a 100644
--- a/server/package.json
+++ b/server/package.json
@@ -20,7 +20,8 @@
     "test:cov": "vitest --config ./infra/vite.config.ts --coverage",
     "test:bench": "vitest bench --config ./infra/vite.config.ts",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-    "test:e2e": "vitest --config test/vite.config.ts"
+    "test:e2e": "vitest --config test/vite.config.ts",
+    "test:preview": "vitest --config ./infra/vite.config.ts --coverage --ui"
   },
   "dependencies": {
     "@nestjs/common": "^11.0.1",
@@ -43,7 +44,8 @@
     "@types/jest": "^29.5.14",
     "@types/node": "^22.10.7",
     "@types/supertest": "^6.0.2",
-    "@vitest/coverage-v8": "^3.0.9",
+    "@vitest/coverage-v8": "^3.1.1",
+    "@vitest/ui": "^3.1.1",
     "eslint": "^9.18.0",
     "eslint-config-prettier": "^10.0.1",
     "eslint-plugin-jsdoc": "^50.6.9",
@@ -60,6 +62,6 @@
     "typescript": "^5.7.3",
     "typescript-eslint": "^8.20.0",
     "unplugin-swc": "^1.5.1",
-    "vitest": "^3.0.9"
+    "vitest": "^3.1.1"
   }
 }


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 255d0f26e49a18d5f9b3d72b8c24f1e39bedd4b4 and e6df7aca2af88b756b94a870d4ef0cbbeb0854be.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `server/src/test.spec.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: Run Unit Tests</summary>

<details>
<summary>server/src/test.spec.ts</summary>

[failure] 3-3: src/test.spec.ts > force fail
AssertionError: expected true to be false // Object.is equality

- Expected
+ Received

- false
+ true

 ❯ src/test.spec.ts:3:15



</details>

</details>
<details>
<summary>🪛 GitHub Actions: Continuous Integration</summary>

<details>
<summary>server/src/test.spec.ts</summary>

[error] 3-3: Test failure: expected true to be false // Object.is equality

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-15T05:12:29Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e6df7aca2af88b756b94a870d4ef0cbbeb0854be and e240bd9fb9251c0a5f5546eb69920a56f170c0aa.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `.github/workflows/deploy-vitest-ui.yaml` (1 hunks)
* `server/infra/vite.config.ts` (1 hunks)
* `server/package.json` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/deploy-vitest-ui.yaml</summary>

22-22: property "ouputs" is not defined in object type {conclusion: string; outcome: string; outputs: {page_url: string}}

(expression)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/deploy-vitest-ui.yaml</summary>

[warning] 3-3: truthy value should be one of [false, true]

(truthy)

---

[error] 45-45: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>server/infra/vite.config.ts (2)</summary>

`4-4`: **Boolean environment variable check improvement**

The change from a strict equality check to a truthiness check (`process.env.GITHUB_ACTIONS === 'true'` to `process.env.GITHUB_ACTIONS`) is a good improvement. Environment variables in Node.js are always strings, and this change will make the check work regardless of the exact string value.

---

`10-10`: **HTML reporter added for GitHub Actions**

Good addition of the 'html' reporter when running in GitHub Actions. This will generate the HTML reports needed for the Vitest UI deployment without affecting local development workflows.

</details>
<details>
<summary>server/package.json (3)</summary>

`23-24`: **New test:preview script for Vitest UI**

The addition of a trailing comma on line 23 and the new `test:preview` script on line 24 look good. The script properly combines coverage and UI flags for Vitest, which will be used by the new GitHub Actions workflow.

---

`47-48`: **Vitest UI dependency added**

Good addition of the `@vitest/ui` dependency and upgrade of `@vitest/coverage-v8` to version 3.1.1.

---

`65-65`: **Vitest dependency updated**

The upgrade of `vitest` to version 3.1.1 aligns with the other Vitest-related dependencies, maintaining consistency across the package versions.

</details>
<details>
<summary>.github/workflows/deploy-vitest-ui.yaml (3)</summary>

`1-8`: **Workflow configuration looks good**

The workflow is properly configured to run on pushes to the main branch and can also be triggered manually via workflow_dispatch.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 3-3: truthy value should be one of [false, true]

(truthy)

</details>

</details>

---

`9-16`: **Appropriate permissions and concurrency settings**

Good job setting the minimum required permissions for GitHub Pages deployment and configuring concurrency to avoid conflicts with other deployments.

---

`24-31`: **Setup steps look good**

The checkout and dependencies installation steps are properly configured.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-16T02:49:02Z

Review comments:
diff_hunk: @@ -0,0 +1,4 @@
+// sample.spec.ts
+test('force fail', () => {
+	expect(true).toBe(false)
+})
path: server/src/test.spec.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix the intentionally failing test causing pipeline failures.**

This test case is explicitly written to fail with `expect(true).toBe(false)`, which is causing CI pipeline failures. Additionally, the comment at the top of the file refers to "sample.spec.ts" while the actual filename is "test.spec.ts".

If this is a temporary test to verify the testing setup, consider one of the following approaches:
1. Update the test to pass, e.g., `expect(true).toBe(true)`
2. Skip the test with `.skip` modifier
3. Mark it as a todo with `.todo`


```diff
-// sample.spec.ts
-test('force fail', () => {
-	expect(true).toBe(false)
-})
+// test.spec.ts
+test('sample test', () => {
+	expect(true).toBe(true)
+})
```

Alternatively, if you want to keep a failing test for demonstration purposes:

```diff
-// sample.spec.ts
-test('force fail', () => {
-	expect(true).toBe(false)
-})
+// test.spec.ts
+test.skip('intentional failure example', () => {
+	expect(true).toBe(false) // This test is intentionally skipped
+})
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// test.spec.ts
test('sample test', () => {
	expect(true).toBe(true)
})
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: Run Unit Tests</summary>

[failure] 3-3: src/test.spec.ts > force fail
AssertionError: expected true to be false // Object.is equality

- Expected
+ Received

- false
+ true

 ❯ src/test.spec.ts:3:15



</details>
<details>
<summary>🪛 GitHub Actions: Continuous Integration</summary>

[error] 3-3: Test failure: expected true to be false // Object.is equality

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-15T05:12:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,45 @@
+name: Deploy Vitest UI to GitHub Pages
+
+on:
+  push:
+    branches:
+      - main
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+concurrency:
+  group: pages
+  cancel-in-progress: false
+
+jobs:
+  deploy:
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.ouputs.page_url }}
+    runs-on: ubuntu-latest
path: .github/workflows/deploy-vitest-ui.yaml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix typo in environment URL output**

There's a typo in the environment URL configuration that will prevent the deployment URL from being properly exposed.


```diff
- url: ${{ steps.deployment.ouputs.page_url }}
+ url: ${{ steps.deployment.outputs.page_url }}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

22-22: property "ouputs" is not defined in object type {conclusion: string; outcome: string; outputs: {page_url: string}}

(expression)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits a0f20b7 to 2e1d3d2
created_at: 2025-04-16T02:49:00Z
author_association: NONE
diff_hunk: @@ -0,0 +1,45 @@
+name: Deploy Vitest UI to GitHub Pages
+
+on:
+  push:
+    branches:
+      - main
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+concurrency:
+  group: pages
+  cancel-in-progress: false
+
+jobs:
+  deploy:
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.ouputs.page_url }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install dependencies
+        working-directory: server
+        run: npm install
+
+      - name: Build Vitest UI
+        run: npm run test:preview
+
+      - name: Setup Pages
+        uses: actions/configure-pages@v5
+
+      - name: Upload artifact
+        uses: actions/upload-pages-artifact@v3
+        with:
+          path: server/html
+
+      - name: Deploy to GitHub Pages
+        id: deployment
+        uses: actions/deploy-pages@v4
path: .github/workflows/deploy-vitest-ui.yaml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Add new line at end of file**

The workflow file is missing a new line character at the end, which is a best practice for text files and highlighted by the linter.


```diff
  - name: Deploy to GitHub Pages
    id: deployment
    uses: actions/deploy-pages@v4
+
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: server/html

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
       
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 45-45: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-16T02:49:00Z
author_association: NONE
diff_hunk: @@ -0,0 +1,45 @@
+name: Deploy Vitest UI to GitHub Pages
+
+on:
+  push:
+    branches:
+      - main
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+concurrency:
+  group: pages
+  cancel-in-progress: false
+
+jobs:
+  deploy:
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.ouputs.page_url }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install dependencies
+        working-directory: server
+        run: npm install
+
+      - name: Build Vitest UI
+        run: npm run test:preview
+
path: .github/workflows/deploy-vitest-ui.yaml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider specifying working directory for Build step**

The Build Vitest UI step should specify the working directory like the previous step to ensure consistency.


```diff
  - name: Build Vitest UI
+   working-directory: server
    run: npm run test:preview
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Build Vitest UI
        working-directory: server
        run: npm run test:preview
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits a0f20b7 to 2e1d3d2
created_at: 2025-04-16T02:49:00Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-15T05:03:44Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This change introduces a new GitHub Actions workflow to automate the deployment of the Vitest UI to GitHub Pages. The `server/package.json` file is updated to include a new script for running Vitest UI and upgrades Vitest-related dependencies. The Vitest configuration in `server/infra/vite.config.ts` is modified to adjust the environment variable check for GitHub Actions and to add the 'html' reporter when running in that environment. No changes are made to exported or public entities in the codebase.

## Changes

| File(s)                                     | Change Summary                                                                                                  |
|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| .github/workflows/deploy-vitest-ui.yaml     | Added a new GitHub Actions workflow for deploying Vitest UI to GitHub Pages.                                    |
| server/package.json                         | Added `test:preview` script, updated `test:e2e` script, upgraded Vitest dependencies, added `@vitest/ui`.       |
| server/infra/vite.config.ts                 | Changed GITHUB_ACTIONS env check to truthiness; added 'html' reporter for GitHub Actions environment.           |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Developer
    participant GitHub Actions
    participant Server (CI)
    participant GitHub Pages

    Developer->>GitHub Actions: Push to main or trigger workflow
    GitHub Actions->>Server (CI): Checkout code, install dependencies
    GitHub Actions->>Server (CI): Run Vitest UI build (npm run test:preview)
    Server (CI)->>Server (CI): Build test UI output to /html
    GitHub Actions->>GitHub Pages: Upload and deploy /html artifact
    GitHub Pages-->>Developer: Serve Vitest UI at deployment URL
```

## Assessment against linked issues

| Objective                                                                 | Addressed | Explanation                                                                 |
|---------------------------------------------------------------------------|-----------|-----------------------------------------------------------------------------|
| Add `--reporter=github` to the Vitest test command in the workflow (#49)  | ✅        |                                                                             |

## Possibly related PRs

- [docker-seminar/docker-tutorial#50](https://github.com/docker-seminar/docker-tutorial/pull/50): Extends the GitHub Actions reporter configuration in `vite.config.ts` by adding an 'html' reporter and changes the environment variable check, closely related to the current PR's config changes.
- [docker-seminar/docker-tutorial#19](https://github.com/docker-seminar/docker-tutorial/pull/19): Introduces initial Vitest migration and related configuration, overlapping with the files and infrastructure updated in this PR.

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡💬 Agentic Chat (Pro Plan, General Availability)</summary>
> 
> - We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e240bd9fb9251c0a5f5546eb69920a56f170c0aa and 2e1d3d2ab47c4fa65e93d480f3526f366af3674f.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `.github/workflows/deploy-vitest-ui.yaml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/deploy-vitest-ui.yaml</summary>

[warning] 3-3: truthy value should be one of [false, true]

(truthy)

---

[error] 46-46: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=docker-seminar/docker-tutorial&utm_content=52):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-15T05:03:48Z
body: > [!WARNING]
  > PR title validation failed!
  ❌Invalid title format.
🔍Expected: <type>(<scope>): <description> or <type>: <description>
🔍Keyword is missing or now lowercase
🔍Scope, if provided, must be enclosed in a parentheses and contain valid characters. (Optional)
🔍The colon separator is missing or incorrect.
🔍 Description is missing after ':'.

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-15T05:04:28Z
body: > [!WARNING]
  > PR title validation failed!
  ❌Invalid title format.
🔍Expected: <type>(<scope>): <description> or <type>: <description>
🔍Keyword is missing or now lowercase
🔍Scope, if provided, must be enclosed in a parentheses and contain valid characters. (Optional)
🔍The colon separator is missing or incorrect.
🔍 Description is missing after ':'.

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-15T05:12:10Z
body: > [!WARNING]
  > PR title validation failed!
  ❌Invalid title format.
🔍Expected: <type>(<scope>): <description> or <type>: <description>
🔍Keyword is missing or now lowercase
🔍Scope, if provided, must be enclosed in a parentheses and contain valid characters. (Optional)
🔍The colon separator is missing or incorrect.
🔍 Description is missing after ':'.

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-16T02:47:03Z
body: > [!WARNING]
  > PR title validation failed!
  🔍Description must start with a capital letter.

author_association: NONE
user: login: kimseungbin
created_at: 2025-04-16T02:47:10Z
body: @coderabbitai full review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-04-16T02:47:14Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Full review triggered.

</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-16T02:48:33Z
body: > [!WARNING]
  > PR title validation failed!
  🔍Description must start with a capital letter.

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-16T02:51:11Z
body: > [!NOTE]
  > PR title validation succeeded.
  ✅Valid title format.
ℹ️**KEYWORD**: infra
ℹ️**SCOPE**: CI
ℹ️**DESCRIPTION**: Add GitHub Actions workflow for Vitest UI deployment to GitHub Pages

author_association: NONE