+
+
+- {{ item.type === 'SONG' ? '鍏蜂綋姝屾洸' : '鍏抽敭璇? }}
++ {{ getTypeLabel(item.type) }}
+
+
+ {{ item.value }}
+@@ -318,7 +335,7 @@
+
+
+
+-
+
+
+diff --git a/app/components/Admin/SchedulePrinter.vue b/app/components/Admin/SchedulePrinter.vue
+index 71bf445..9b26695 100644
+--- a/app/components/Admin/SchedulePrinter.vue
++++ b/app/components/Admin/SchedulePrinter.vue
+@@ -1383,15 +1383,15 @@ watch(
+ /* 棰勮鍖哄煙鍜屾墦鍗版牱寮忕殑鏍稿績CSS - 淇濇寔鍘熺敓CSS浠ョ‘淇濈簿纭帶鍒?*/
+
+ .preview-content {
+- background: #ffffff;
++ background: var(--card-bg, #ffffff);
+ padding: 0;
+ position: relative;
+ }
+
+ /* 鎵撳嵃椤甸潰鏍峰紡 */
+ .print-page {
+- background: #ffffff;
+- color: #000000;
++ background: var(--card-bg, #ffffff);
++ color: var(--text-primary, #000000);
+ width: 100%;
+ margin: 0 auto;
+ padding: 30px;
+@@ -1426,7 +1426,7 @@ watch(
+ .logo-divider {
+ width: 2px;
+ height: 60px;
+- background: linear-gradient(to bottom, #ddd, #999, #ddd);
++ background: linear-gradient(to bottom, var(--border-secondary, #dddddd), #999, #ddd);
+ border-radius: 1px;
+ margin: 0 4px;
+ }
+@@ -1445,19 +1445,19 @@ watch(
+ font-size: 24px;
+ font-weight: normal;
+ margin: 0 0 4px 0;
+- color: #000;
++ color: var(--text-primary, #000);
+ }
+
+ .title-section h2 {
+ font-size: 18px;
+ font-weight: 500;
+ margin: 0;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ }
+
+ .date-info {
+ font-size: 14px;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ text-align: right;
+ display: flex;
+ align-items: flex-start;
+@@ -1477,14 +1477,14 @@ watch(
+ .no-data-message {
+ text-align: center;
+ padding: 60px 20px;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ }
+
+ .no-data-icon {
+ width: 64px;
+ height: 64px;
+ margin: 0 auto 20px;
+- color: #ccc;
++ color: var(--text-secondary, #cccccc);
+ }
+
+ .no-data-icon svg {
+@@ -1496,7 +1496,7 @@ watch(
+ font-size: 18px;
+ font-weight: 500;
+ margin: 0 0 12px 0;
+- color: #333;
++ color: var(--text-quaternary, #333333);
+ }
+
+ .no-data-message p {
+@@ -1512,15 +1512,15 @@ watch(
+ font-weight: bold;
+ margin: 24px 0 12px 0;
+ padding: 8px 12px;
+- border-bottom: 2px solid #ddd;
++ border-bottom: 2px solid var(--border-secondary, #ddd);
+ background: #f8f9fa;
+- color: #333;
++ color: var(--text-quaternary, #333333);
+ border-radius: 4px 4px 0 0;
+ }
+
+ .playtime-group .group-title {
+ background: #e3f2fd;
+- border-bottom-color: #2196f3;
++ border-bottom-color: var(--border-secondary, #2196f3);
+ color: #1565c0;
+ }
+
+@@ -1533,7 +1533,7 @@ watch(
+ .group-count {
+ font-size: 14px;
+ font-weight: normal;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ margin-left: 8px;
+ }
+
+@@ -1547,7 +1547,7 @@ watch(
+ margin: 16px 0 8px 0;
+ padding: 6px 10px;
+ background: #f0f8ff;
+- border-left: 3px solid #2196f3;
++ border-left: 3px solid var(--border-secondary, #2196f3);
+ color: #1565c0;
+ border-radius: 0 4px 4px 0;
+ }
+@@ -1555,7 +1555,7 @@ watch(
+ .playtime-count {
+ font-size: 12px;
+ font-weight: normal;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ margin-left: 6px;
+ }
+
+@@ -1570,7 +1570,7 @@ watch(
+ padding-top: 16px;
+ border-top: 1px solid #e5e5e5;
+ font-size: 12px;
+- color: #666;
++ color: var(--text-quaternary, #666666);
+ }
+
+ .footer-left {
+diff --git a/app/components/Admin/Sidebar.vue b/app/components/Admin/Sidebar.vue
+index d4dc22a..369f26c 100644
+--- a/app/components/Admin/Sidebar.vue
++++ b/app/components/Admin/Sidebar.vue
+@@ -279,7 +279,7 @@ const getRoleDisplayName = (role) => {
+ background: transparent;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb {
+- background: #27272a;
++ background: var(--bg-tertiary, #27272a);
+ border-radius: 10px;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+diff --git a/app/components/Admin/SongDownloadDialog.vue b/app/components/Admin/SongDownloadDialog.vue
+index bb81bf2..9268c5a 100644
+--- a/app/components/Admin/SongDownloadDialog.vue
++++ b/app/components/Admin/SongDownloadDialog.vue
+@@ -1228,7 +1228,7 @@ onUnmounted(() => {
+ background: transparent;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb {
+- background: #27272a;
++ background: var(--bg-tertiary, #27272a);
+ border-radius: 10px;
+ }
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+diff --git a/app/components/Admin/SongManagement.vue b/app/components/Admin/SongManagement.vue
+index ad4445c..a630cc2 100644
+--- a/app/components/Admin/SongManagement.vue
++++ b/app/components/Admin/SongManagement.vue
+@@ -61,6 +61,12 @@
+ >
+
鏈挱鏀?({{ selectedSongs.length }})
+
++
++ 椹冲洖 ({{ selectedSongs.length }})
++
+
+
+-
椹冲洖姝屾洸
++
{{ isBatchReject ? `鎵归噺椹冲洖姝屾洸 (${selectedSongs.length} 棣?` : '椹冲洖姝屾洸' }}
+
+
+
+-
++
+
+@@ -429,6 +435,17 @@
+
鎶曠浜? {{ rejectSongInfo.requester }}
+
+
++
++
++
++
++
++
鍗冲皢鎵归噺椹冲洖 {{ selectedSongs.length }} 棣栨瓕鏇?/h4>
++ 椹冲洖鍚庡皢涓€骞舵竻鐞嗘姇绁ㄣ€佹帓鏈熻褰曞苟鍙戦€侀€氱煡缁欏搴旀姇绋夸汉
++
++
+
+
+
+
+
+-
++
++
+diff --git a/app/components/Auth/ProfileForm.vue b/app/components/Auth/ProfileForm.vue
+index 3c23d2d..1403f16 100644
+--- a/app/components/Auth/ProfileForm.vue
++++ b/app/components/Auth/ProfileForm.vue
+@@ -224,24 +224,24 @@ const handleUpdateProfile = async () => {
+
+diff --git a/app/components/SiteFooter.vue b/app/components/SiteFooter.vue
+index 0b3adc7..c5d528d 100644
+--- a/app/components/SiteFooter.vue
++++ b/app/components/SiteFooter.vue
+@@ -23,11 +23,16 @@
+
+
+
+-
++
+
+
+
+
+@@ -578,15 +571,6 @@
+ @confirm="handleConfirmAction"
+ />
+
+-
+-
+-
+-
+
+
+
++
++
+diff --git a/app/pages/index.vue b/app/pages/index.vue
+index 33fe93f..7ec75dd 100644
+--- a/app/pages/index.vue
++++ b/app/pages/index.vue
+@@ -33,6 +33,18 @@
+
+
+
++
++
++
++ 涓婚
++
++
+
+
+ {{ user?.name || '鐢ㄦ埛' }}
+@@ -653,6 +665,13 @@
+
+
+
++
++
+
+
+
+@@ -667,12 +686,19 @@ import ConfirmDialog from '~/components/UI/ConfirmDialog.vue'
+
+ import { useNotifications } from '~/composables/useNotifications'
+ import { useSiteConfig } from '~/composables/useSiteConfig'
++import { useTheme } from '~/composables/useTheme'
+ import CustomSelect from '~/components/UI/Common/CustomSelect.vue'
+
+ // 鑾峰彇杩愯鏃堕厤缃? const config = useRuntimeConfig()
+ const router = useRouter()
+
++// 涓婚
++const { isDark, toggleTheme } = useTheme()
++const handleToggleTheme = () => {
++ toggleTheme()
++}
++
+ // 绔欑偣閰嶇疆
+ const {
+ siteTitle,
+@@ -680,6 +706,8 @@ const {
+ guidelines: submissionGuidelines,
+ icp: icpNumber,
+ schoolLogoHomeUrl,
++ statisticsCode,
++ statisticsCodeEnabled,
+ initSiteConfig
+ } = useSiteConfig()
+
+@@ -1174,6 +1202,15 @@ onMounted(async () => {
+ }
+ })
+ }
++
++ // 娉ㄥ叆绔欑偣缁熻浠g爜锛堝厹搴曟柟妗堬細鍔ㄦ€佹彃鍏?script 鍒?body 鏈熬锛?+ if (statisticsCodeEnabled.value && statisticsCode.value) {
++ try {
++ injectStatisticsCode(statisticsCode.value)
++ } catch (e) {
++ console.warn('缁熻浠g爜娉ㄥ叆澶辫触:', e)
++ }
++ }
+ } catch (error) {
+ console.error('棣栭〉鍒濆鍖栧け璐?', error)
+ await Promise.allSettled([songs.fetchPublicSchedules(), songs.fetchSongCount()])
+@@ -1522,6 +1559,38 @@ const showLoginNotice = () => {
+ }
+ }
+
++// 娉ㄥ叆绔欑偣缁熻浠g爜锛堝厹搴曟柟妗堬細鍔ㄦ€佽В鏋愬苟鎻掑叆 script 鍏冪礌鍒?body 鏈熬锛?+const injectStatisticsCode = (code) => {
++ if (typeof document === 'undefined' || !code) return
++
++ // 鍏堝皾璇曠敤涓存椂瀹瑰櫒瑙f瀽 HTML锛屾彁鍙栧叾涓殑 script 鏍囩
++ const tempDiv = document.createElement('div')
++ tempDiv.innerHTML = code
++
++ // 灏嗛潪 script 鐨勫厓绱犲厛闄勫姞鍒板鍣ㄤ腑
++ const container = document.getElementById('site-statistics-code')
++ if (container) {
++ Array.from(tempDiv.childNodes).forEach((node) => {
++ if (node.nodeType === 1 && node.tagName && node.tagName.toLowerCase() !== 'script') {
++ container.appendChild(node.cloneNode(true))
++ }
++ })
++ }
++
++ // 鍗曠嫭澶勭悊 script 鏍囩锛氬姩鎬佸垱寤哄苟鎻掑叆鍒?body 鏈熬锛岀‘淇濊兘姝g‘鎵ц
++ const scripts = tempDiv.querySelectorAll('script')
++ scripts.forEach((oldScript) => {
++ const newScript = document.createElement('script')
++ Array.from(oldScript.attributes).forEach((attr) => {
++ newScript.setAttribute(attr.name, attr.value)
++ })
++ if (oldScript.textContent) {
++ newScript.textContent = oldScript.textContent
++ }
++ document.body.appendChild(newScript)
++ })
++}
++
+ // 妫€鏌ョ敤鎴锋槸鍚﹂渶瑕佷慨鏀瑰瘑鐮? const checkPasswordChangeRequired = async (user = null) => {
+ try {
+@@ -1564,9 +1633,9 @@ if (
+ .home {
+ width: 100%;
+ flex: 1;
+- background-color: #121318;
++ background-color: var(--bg-primary, #ffffff);
+ padding: 1.5rem;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh; /* 纭繚鑷冲皯鍗犳弧瑙嗗彛 */
+@@ -1640,9 +1709,9 @@ if (
+ height: 100px;
+ background: linear-gradient(
+ to bottom,
+- rgba(255, 255, 255, 0.3),
+- rgba(255, 255, 255, 0.8),
+- rgba(255, 255, 255, 0.3)
++ var(--border-secondary, #d1d5db),
++ var(--border-tertiary, #9ca3af),
++ var(--border-secondary, #d1d5db)
+ );
+ border-radius: 1px;
+ }
+@@ -1659,6 +1728,53 @@ if (
+ .user-section {
+ position: relative;
+ z-index: 100;
++ display: flex;
++ align-items: center;
++ gap: 10px;
++}
++
++/* 閫氱敤宸ュ叿鎸夐挳锛堜富棰橈級 */
++.utility-btn {
++ display: inline-flex;
++ align-items: center;
++ gap: 6px;
++ padding: 7px 12px;
++ background: var(--surface-secondary, var(--bg-secondary));
++ color: var(--text-primary);
++ border: 1px solid var(--border-primary);
++ border-radius: 10px;
++ font-size: 13px;
++ font-weight: 500;
++ line-height: 1;
++ cursor: pointer;
++ transition:
++ background 0.2s ease,
++ color 0.2s ease,
++ border-color 0.2s ease,
++ transform 0.15s ease;
++}
++
++.utility-btn:hover {
++ background: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--border-secondary);
++}
++
++.utility-btn:active {
++ transform: scale(0.97);
++}
++
++.utility-btn-label {
++ white-space: nowrap;
++}
++
++@media (max-width: 768px) {
++ .utility-btn-label {
++ display: none;
++ }
++ .utility-btn {
++ padding: 7px 8px;
++ border-radius: 9px;
++ }
+ }
+
+ .user-info {
+@@ -1679,9 +1795,9 @@ if (
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+- background: rgba(59, 130, 246, 0.15);
+- color: #60a5fa;
+- border: 1px solid rgba(59, 130, 246, 0.3);
++ background: var(--primary-light, rgba(30, 64, 175, 0.08));
++ color: var(--primary, #1e40af);
++ border: 1px solid var(--primary-border, rgba(30, 64, 175, 0.15));
+ border-radius: 8px;
+ font-size: 0.85rem;
+ font-weight: 600;
+@@ -1692,7 +1808,7 @@ if (
+ }
+
+ .enter-admin-btn:hover {
+- background: rgba(59, 130, 246, 0.25);
++ background: var(--primary-border, rgba(30, 64, 175, 0.15));
+ transform: translateY(-1px);
+ }
+
+@@ -1705,21 +1821,21 @@ if (
+ .user-name {
+ font-size: 14px;
+ font-weight: 600;
+- color: rgba(255, 255, 255, 0.9);
++ color: var(--text-primary, #1a2332);
+ }
+
+ .user-badge {
+ font-size: 10px;
+ padding: 1px 6px;
+- background: rgba(255, 255, 255, 0.1);
+- color: rgba(255, 255, 255, 0.5);
++ background: var(--surface-tertiary, var(--bg-tertiary));
++ color: var(--text-tertiary, #6b7280);
+ border-radius: 4px;
+ margin-top: 2px;
+ }
+
+ .user-badge.admin {
+- background: rgba(59, 130, 246, 0.2);
+- color: #3b82f6;
++ background: var(--primary-light, rgba(30, 64, 175, 0.08));
++ color: var(--primary, #1e40af);
+ }
+
+ .user-avatar-wrapper {
+@@ -1727,8 +1843,8 @@ if (
+ height: 36px;
+ border-radius: 50%;
+ overflow: hidden;
+- background: #2a2a2a;
+- border: 1px solid rgba(255, 255, 255, 0.1);
++ background: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+@@ -1750,12 +1866,12 @@ if (
+ position: absolute;
+ top: calc(100% + 12px);
+ right: 0;
+- background: #1a1a1f;
+- border: 1px solid rgba(255, 255, 255, 0.1);
++ background: var(--surface-primary, var(--bg-primary));
++ border: 1px solid var(--border-primary, #e5e7eb);
+ border-radius: 12px;
+ padding: 8px;
+ min-width: 160px;
+- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
++ box-shadow: var(--shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+@@ -1767,7 +1883,7 @@ if (
+ gap: 10px;
+ padding: 10px 12px;
+ border-radius: 8px;
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ font-size: 14px;
+ transition: all 0.2s;
+ text-decoration: none;
+@@ -1777,13 +1893,13 @@ if (
+ }
+
+ .action-item:hover {
+- background: rgba(255, 255, 255, 0.05);
+- color: white;
++ background: var(--surface-secondary, var(--bg-secondary));
++ color: var(--text-primary, #1a2332);
+ }
+
+ .action-item.logout:hover {
+- background: rgba(239, 68, 68, 0.1);
+- color: #ef4444;
++ background: var(--error-light, rgba(239, 68, 68, 0.1));
++ color: var(--error, #ef4444);
+ }
+
+ .notification-badge {
+@@ -1793,10 +1909,10 @@ if (
+ min-width: 18px;
+ height: 18px;
+ border-radius: 9px;
+- background-color: #f44336;
+- color: white;
++ background-color: var(--error, #ef4444);
++ color: var(--text-primary, #ffffff);
+ font-size: 10px;
+- font-weight: bold;
++ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+@@ -1812,12 +1928,12 @@ if (
+ border-radius: 14px;
+ font-size: 14px;
+ font-weight: 600;
+- background: rgba(255, 255, 255, 0.04);
++ background: var(--surface-secondary, var(--bg-secondary));
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+- color: #ffffff;
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
++ color: var(--text-primary, #1a2332);
++ border: 1px solid var(--border-primary, #e5e7eb);
++ box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.05));
+ transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
+ text-decoration: none;
+ position: relative;
+@@ -1825,23 +1941,23 @@ if (
+ }
+
+ .login-options .login-btn:hover {
+- background: rgba(255, 255, 255, 0.08);
+- border-color: rgba(59, 130, 246, 0.5);
++ background: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--primary-border, rgba(30, 64, 175, 0.15));
+ transform: translateY(-2px);
+- box-shadow: 0 8px 25px rgba(59, 130, 246, 0.2);
+- color: white;
++ box-shadow: var(--shadow-md, 0 4px 6px rgba(0, 0, 0, 0.1));
++ color: var(--text-primary, #1a2332);
+ }
+
+ .login-options .login-btn :deep(.icon),
+ .login-options .login-btn i {
+- color: #3b82f6;
++ color: var(--primary, #1e40af);
+ transition: all 0.3s ease;
+ }
+
+ .login-options .login-btn:hover :deep(.icon),
+ .login-options .login-btn:hover i {
+ transform: scale(1.1);
+- filter: drop-shadow(0 0 5px rgba(59, 130, 246, 0.5));
++ filter: drop-shadow(0 0 5px rgba(30, 64, 175, 0.3));
+ }
+
+ .login-options .login-btn:active {
+@@ -1868,10 +1984,9 @@ if (
+ font-weight: 800;
+ font-size: 42px;
+ letter-spacing: -0.02em;
+- background: linear-gradient(135deg, #ffffff 0%, rgba(255, 255, 255, 0.7) 100%);
++ background: linear-gradient(135deg, var(--text-primary, #1a2332) 0%, var(--text-secondary, #4a5568) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+- text-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+ margin: 0;
+ line-height: 1.2;
+ }
+@@ -1879,16 +1994,16 @@ if (
+ .title-divider {
+ width: 40px;
+ height: 4px;
+- background: #0b5afe;
++ background: var(--primary, #1e40af);
+ border-radius: 2px;
+- box-shadow: 0 0 15px rgba(11, 90, 254, 0.6);
++ box-shadow: 0 0 15px var(--primary-light, rgba(30, 64, 175, 0.08));
+ }
+
+ .sub-title {
+ font-family: 'MiSans', sans-serif;
+ font-weight: 500;
+ font-size: 14px;
+- color: rgba(255, 255, 255, 0.4);
++ color: var(--text-quaternary, #9ca3af);
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ }
+@@ -1920,22 +2035,22 @@ if (
+ }
+
+ .section-tab {
+- background: #1a1b24;
++ background: var(--surface-secondary, var(--bg-secondary));
+ border-radius: 15px 15px 0 0;
+ padding: 15px 24px;
+ font-family: 'MiSans', sans-serif;
+ font-weight: 600;
+ font-size: 16px;
+- color: rgba(255, 255, 255, 0.8);
+- border: 2px solid #282830;
++ color: var(--text-secondary, #4a5568);
++ border: 2px solid var(--border-primary, #e5e7eb);
+ border-bottom: none;
+ cursor: pointer;
+ flex: 0 0 auto;
+ }
+
+ .section-tab.active {
+- background: #21242d;
+- color: #ffffff;
++ background: var(--surface-primary, var(--bg-primary));
++ color: var(--text-primary, #1a2332);
+ position: relative;
+ z-index: 1;
+ }
+@@ -2028,9 +2143,9 @@ if (
+ right: -8px;
+ width: 6px;
+ height: 6px;
+- background: #0b5afe;
++ background: var(--primary, #1e40af);
+ border-radius: 50%;
+- box-shadow: 0 0 5px rgba(11, 90, 254, 0.5);
++ box-shadow: 0 0 5px var(--primary-light, rgba(30, 64, 175, 0.08));
+ }
+
+ .section-tab::after {
+@@ -2040,7 +2155,7 @@ if (
+ left: 50%;
+ width: 0;
+ height: 2px;
+- background: #0b5afe;
++ background: var(--primary, #1e40af);
+ transition: all 0.3s ease;
+ transform: translateX(-50%);
+ }
+@@ -2058,19 +2173,19 @@ if (
+ transform: none; /* 绉婚櫎涓婃诞鏁堟灉 */
+ background-color: transparent; /* 绉婚櫎鑳屾櫙鑹?*/
+ box-shadow: none; /* 绉婚櫎鍐呴槾褰?*/
+- color: rgba(255, 255, 255, 0.9);
++ color: var(--text-primary, #1a2332);
+ }
+
+ .section-tab.active:hover {
+ background-color: transparent;
+ box-shadow: none;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ }
+
+ /* 鍐呭瀹瑰櫒 */
+ .tab-content-container {
+- background: #1a1b24;
+- border: 2px solid #282830;
++ background: var(--surface-primary, var(--bg-primary));
++ border: 2px solid var(--border-primary, #e5e7eb);
+ border-radius: 0 15px 15px 15px;
+ padding: 1.5rem;
+ margin-top: -2px; /* 浣垮唴瀹瑰鍣ㄤ笌鏍囩杩炴帴 */
+@@ -2166,7 +2281,7 @@ if (
+ font-weight: 400;
+ font-size: 16px;
+ letter-spacing: 4%;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-tertiary, #6b7280);
+ margin: 1.5rem 0 1rem;
+ }
+
+@@ -2186,7 +2301,7 @@ if (
+ .empty-state {
+ text-align: center;
+ padding: 2rem;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-tertiary, #6b7280);
+ }
+
+ /* 涓嬫媺鑿滃崟鍔ㄧ敾 */
+@@ -2223,13 +2338,13 @@ if (
+ align-items: center;
+ padding: 1.25rem 0;
+ margin-bottom: 1.5rem;
+- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
++ border-bottom: 1px solid var(--border-primary, #e5e7eb);
+ }
+
+ .notification-title {
+ font-size: 1.25rem;
+ font-weight: 700;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ margin: 0;
+ display: flex;
+ align-items: center;
+@@ -2237,9 +2352,9 @@ if (
+ }
+
+ .settings-icon {
+- background: rgba(255, 255, 255, 0.05);
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- color: rgba(255, 255, 255, 0.6);
++ background: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
++ color: var(--text-tertiary, #6b7280);
+ width: 36px;
+ height: 36px;
+ border-radius: 10px;
+@@ -2251,8 +2366,8 @@ if (
+ }
+
+ .settings-icon:hover {
+- background-color: rgba(255, 255, 255, 0.1);
+- color: #ffffff;
++ background-color: var(--surface-tertiary, var(--bg-tertiary));
++ color: var(--text-primary, #1a2332);
+ transform: rotate(30deg);
+ }
+
+@@ -2275,14 +2390,14 @@ if (
+ align-items: center;
+ justify-content: center;
+ padding: 3rem 0;
+- color: rgba(255, 255, 255, 0.4);
++ color: var(--text-quaternary, #9ca3af);
+ gap: 1.25rem;
+ }
+
+ .empty-icon {
+ width: 80px;
+ height: 80px;
+- background: rgba(255, 255, 255, 0.03);
++ background: var(--surface-secondary, var(--bg-secondary));
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+@@ -2293,8 +2408,8 @@ if (
+ .loading-spinner {
+ width: 32px;
+ height: 32px;
+- border: 3px solid rgba(255, 255, 255, 0.1);
+- border-top-color: #3b82f6;
++ border: 3px solid var(--border-secondary, #d1d5db);
++ border-top-color: var(--primary, #1e40af);
+ border-radius: 50%;
+ animation: spin 0.8s linear infinite;
+ }
+@@ -2312,9 +2427,8 @@ if (
+ }
+
+ .notification-card {
+- background: rgba(255, 255, 255, 0.03);
+- backdrop-filter: blur(20px);
+- border: 1px solid rgba(255, 255, 255, 0.06);
++ background: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
+ border-radius: 20px;
+ padding: 1.25rem;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+@@ -2323,14 +2437,14 @@ if (
+ }
+
+ .notification-card:hover {
+- background: rgba(255, 255, 255, 0.06);
+- border-color: rgba(255, 255, 255, 0.12);
++ background: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--border-secondary, #d1d5db);
+ transform: translateY(-2px);
+ }
+
+ .notification-card.unread {
+- background: rgba(59, 130, 246, 0.05);
+- border-color: rgba(59, 130, 246, 0.2);
++ background: var(--primary-light, rgba(30, 64, 175, 0.08));
++ border-color: var(--primary-border, rgba(30, 64, 175, 0.15));
+ }
+
+ .notification-card-header {
+@@ -2345,8 +2459,8 @@ if (
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+- background: rgba(59, 130, 246, 0.1);
+- color: #3b82f6;
++ background: var(--primary-light, rgba(30, 64, 175, 0.08));
++ color: var(--primary, #1e40af);
+ border-radius: 12px;
+ margin-right: 1rem;
+ flex-shrink: 0;
+@@ -2361,13 +2475,13 @@ if (
+ .notification-title {
+ font-size: 0.95rem;
+ font-weight: 600;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ margin-bottom: 0.25rem;
+ }
+
+ .notification-time {
+ font-size: 0.75rem;
+- color: rgba(255, 255, 255, 0.4);
++ color: var(--text-quaternary, #9ca3af);
+ }
+
+ .notification-card-body {
+@@ -2375,7 +2489,7 @@ if (
+ }
+
+ .notification-text {
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ font-size: 0.875rem;
+ line-height: 1.6;
+ }
+@@ -2388,9 +2502,9 @@ if (
+ }
+
+ .action-button.delete {
+- background: rgba(239, 68, 68, 0.05);
+- color: #ef4444;
+- border: 1px solid rgba(239, 68, 68, 0.1);
++ background: var(--error-light, rgba(239, 68, 68, 0.1));
++ color: var(--error, #ef4444);
++ border: 1px solid var(--error-border, rgba(239, 68, 68, 0.2));
+ padding: 0.4rem 0.75rem;
+ border-radius: 8px;
+ font-size: 0.75rem;
+@@ -2401,22 +2515,22 @@ if (
+ }
+
+ .action-button.delete:hover {
+- background: rgba(239, 68, 68, 0.1);
+- border-color: rgba(239, 68, 68, 0.2);
++ background: rgba(239, 68, 68, 0.15);
++ border-color: rgba(239, 68, 68, 0.3);
+ }
+
+ .notification-actions-bar {
+ display: flex;
+ gap: 1rem;
+ padding: 1rem 0;
+- border-top: 1px solid rgba(255, 255, 255, 0.08);
++ border-top: 1px solid var(--border-primary, #e5e7eb);
+ }
+
+ .action-button-large {
+ flex: 1;
+- background: rgba(255, 255, 255, 0.05);
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- color: #ffffff;
++ background: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
++ color: var(--text-primary, #1a2332);
+ padding: 0.75rem;
+ border-radius: 12px;
+ font-size: 0.875rem;
+@@ -2426,15 +2540,15 @@ if (
+ }
+
+ .action-button-large:hover:not(.disabled) {
+- background: rgba(255, 255, 255, 0.1);
++ background: var(--surface-tertiary, var(--bg-tertiary));
+ }
+
+ .action-button-large.danger {
+- color: #ef4444;
++ color: var(--error, #ef4444);
+ }
+
+ .action-button-large.danger:hover {
+- background: rgba(239, 68, 68, 0.1);
++ background: var(--error-light, rgba(239, 68, 68, 0.1));
+ }
+
+ .action-button-large.disabled {
+@@ -2445,8 +2559,8 @@ if (
+ /* 鍒嗛〉鎺т欢鏍峰紡 */
+ .notification-pagination {
+ padding: 15px 0;
+- border-top: 1px solid rgba(255, 255, 255, 0.1);
+- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
++ border-top: 1px solid var(--border-primary, #e5e7eb);
++ border-bottom: 1px solid var(--border-primary, #e5e7eb);
+ }
+
+ .pagination-info {
+@@ -2455,7 +2569,7 @@ if (
+ }
+
+ .pagination-text {
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ font-size: 0.85rem;
+ }
+
+@@ -2473,7 +2587,7 @@ if (
+ }
+
+ .page-size-selector label {
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ font-size: 0.85rem;
+ white-space: nowrap;
+ }
+@@ -2489,9 +2603,9 @@ if (
+ }
+
+ .page-nav-button {
+- background-color: rgba(255, 255, 255, 0.1);
+- border: 1px solid rgba(255, 255, 255, 0.2);
+- color: var(--light);
++ background-color: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
++ color: var(--text-primary, #1a2332);
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+@@ -2504,8 +2618,8 @@ if (
+ }
+
+ .page-nav-button:hover:not(:disabled) {
+- background-color: rgba(255, 255, 255, 0.2);
+- border-color: rgba(255, 255, 255, 0.3);
++ background-color: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--border-secondary, #d1d5db);
+ transform: translateY(-1px);
+ }
+
+@@ -2515,9 +2629,9 @@ if (
+ }
+
+ .page-nav-button:disabled {
+- background-color: rgba(255, 255, 255, 0.05);
+- border-color: rgba(255, 255, 255, 0.1);
+- color: rgba(255, 255, 255, 0.3);
++ background-color: var(--surface-secondary, var(--bg-secondary));
++ border-color: var(--border-primary, #e5e7eb);
++ color: var(--text-disabled, #c5cbd3);
+ cursor: not-allowed;
+ }
+
+@@ -2528,9 +2642,9 @@ if (
+ }
+
+ .page-number-button {
+- background-color: rgba(255, 255, 255, 0.1);
+- border: 1px solid rgba(255, 255, 255, 0.2);
+- color: var(--light);
++ background-color: var(--surface-secondary, var(--bg-secondary));
++ border: 1px solid var(--border-primary, #e5e7eb);
++ color: var(--text-primary, #1a2332);
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+@@ -2546,8 +2660,8 @@ if (
+ }
+
+ .page-number-button:hover:not(:disabled) {
+- background-color: rgba(255, 255, 255, 0.2);
+- border-color: rgba(255, 255, 255, 0.3);
++ background-color: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--border-secondary, #d1d5db);
+ transform: translateY(-1px);
+ }
+
+@@ -2559,23 +2673,23 @@ if (
+ .page-number-button.active {
+ background-color: var(--primary);
+ border-color: var(--primary);
+- color: white;
++ color: var(--btn-primary-text, #ffffff);
+ }
+
+ .page-number-button.active:hover {
+- background-color: #0952e8;
+- border-color: #0952e8;
++ background-color: var(--primary-hover, #1e3a8a);
++ border-color: var(--primary-hover, #1e3a8a);
+ }
+
+ .page-number-button:disabled {
+- background-color: rgba(255, 255, 255, 0.05);
+- border-color: rgba(255, 255, 255, 0.1);
+- color: rgba(255, 255, 255, 0.3);
++ background-color: var(--surface-secondary, var(--bg-secondary));
++ border-color: var(--border-primary, #e5e7eb);
++ color: var(--text-disabled, #c5cbd3);
+ cursor: not-allowed;
+ }
+
+ .page-ellipsis {
+- color: rgba(255, 255, 255, 0.5);
++ color: var(--text-tertiary, #6b7280);
+ padding: 0 4px;
+ font-size: 0.85rem;
+ }
+@@ -2587,7 +2701,7 @@ if (
+ justify-content: center;
+ gap: 8px;
+ padding: 10px 0;
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ font-size: 0.85rem;
+ animation: fade-in 0.3s ease;
+ }
+@@ -2595,7 +2709,7 @@ if (
+ .loading-spinner {
+ width: 16px;
+ height: 16px;
+- border: 2px solid rgba(255, 255, 255, 0.2);
++ border: 2px solid var(--border-secondary, #d1d5db);
+ border-top: 2px solid var(--primary);
+ border-radius: 50%;
+ animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
+@@ -2628,13 +2742,13 @@ if (
+ justify-content: center;
+ gap: 10px;
+ padding: 15px 0;
+- border-top: 1px solid rgba(255, 255, 255, 0.1);
++ border-top: 1px solid var(--border-primary, #e5e7eb);
+ }
+
+ .action-button-large {
+- background-color: rgba(255, 255, 255, 0.1);
++ background-color: var(--surface-secondary, var(--bg-secondary));
+ border: none;
+- color: var(--light);
++ color: var(--text-primary, #1a2332);
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+@@ -2643,26 +2757,26 @@ if (
+ }
+
+ .action-button-large:hover {
+- background-color: rgba(255, 255, 255, 0.2);
++ background-color: var(--surface-tertiary, var(--bg-tertiary));
+ }
+
+ .action-button-large.danger {
+- color: #ef4444;
++ color: var(--error, #ef4444);
+ }
+
+ .action-button-large.danger:hover {
+- background-color: rgba(239, 68, 68, 0.2);
++ background-color: var(--error-light, rgba(239, 68, 68, 0.1));
+ }
+
+ .action-button-large.disabled {
+- background-color: rgba(255, 255, 255, 0.05);
+- color: rgba(255, 255, 255, 0.3);
++ background-color: var(--surface-secondary, var(--bg-secondary));
++ color: var(--text-disabled, #c5cbd3);
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+
+ .action-button-large.disabled:hover {
+- background-color: rgba(255, 255, 255, 0.05);
++ background-color: var(--surface-secondary, var(--bg-secondary));
+ }
+
+ /* ==================== 绉诲姩绔璁?==================== */
+@@ -2671,7 +2785,7 @@ if (
+ @media (max-width: 768px) {
+ .home {
+ padding: 0;
+- background-color: #0a0a0f;
++ background-color: var(--bg-primary, #ffffff);
+ }
+
+ .main-content {
+@@ -2690,8 +2804,8 @@ if (
+ align-items: center;
+ padding: 10px 12px;
+ margin: 0;
+- background: linear-gradient(180deg, rgba(11, 90, 254, 0.08) 0%, transparent 100%);
+- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
++ background: linear-gradient(180deg, var(--primary-light, rgba(30, 64, 175, 0.08)) 0%, transparent 100%);
++ border-bottom: 1px solid var(--border-primary, #e5e7eb);
+ }
+
+ .logo-section {
+@@ -2711,7 +2825,7 @@ if (
+ .logo-divider {
+ height: 28px;
+ width: 1px;
+- background: rgba(255, 255, 255, 0.15);
++ background: var(--border-secondary, #d1d5db);
+ }
+
+ /* 绉诲姩绔殑涓婚〉闈㈤噷涓嶉渶瑕佸啓瀛︽牎鍚嶏紝淇濇寔绠€娲?*/
+@@ -2736,8 +2850,8 @@ if (
+ .user-avatar-wrapper {
+ width: 32px;
+ height: 32px;
+- background: rgba(255, 255, 255, 0.05);
+- border-color: rgba(255, 255, 255, 0.1);
++ background: var(--surface-secondary, var(--bg-secondary));
++ border-color: var(--border-primary, #e5e7eb);
+ }
+
+ .user-avatar-placeholder {
+@@ -2764,23 +2878,23 @@ if (
+ border-radius: 12px;
+ font-size: 14px;
+ font-weight: 600;
+- background: rgba(255, 255, 255, 0.08);
++ background: var(--surface-secondary, var(--bg-secondary));
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+- color: white;
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
++ color: var(--text-primary, #1a2332);
++ border: 1px solid var(--border-primary, #e5e7eb);
++ box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.05));
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ }
+
+ .login-options .login-btn :deep(.icon) {
+- color: #3b82f6;
++ color: var(--primary, #1e40af);
+ }
+
+ .login-options .login-btn:active {
+ transform: scale(0.95);
+- background: rgba(255, 255, 255, 0.15);
+- border-color: rgba(59, 130, 246, 0.4);
++ background: var(--surface-tertiary, var(--bg-tertiary));
++ border-color: var(--primary-border, rgba(30, 64, 175, 0.15));
+ }
+
+ /* Footer 闂磋窛浼樺寲 */
+@@ -2809,13 +2923,13 @@ if (
+ gap: 0;
+ padding: 0 0.5rem;
+ height: 64px;
+- background: rgba(28, 28, 30, 0.9);
++ background: var(--surface-primary, var(--bg-primary));
+ backdrop-filter: blur(20px) saturate(180%);
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
+- border: 1px solid rgba(255, 255, 255, 0.1);
++ border: 1px solid var(--border-primary, #e5e7eb);
+ border-radius: 9999px;
+ z-index: 1000;
+- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
++ box-shadow: var(--shadow-xl, 0 20px 25px rgba(0, 0, 0, 0.1));
+ }
+
+ .section-tab {
+@@ -2827,7 +2941,7 @@ if (
+ padding: 0; /* 绉婚櫎鍥哄畾鍐呰竟璺濓紝鏀圭敤 flex 灞呬腑 */
+ font-size: 10px;
+ font-weight: 500;
+- color: #71717a; /* text-zinc-500 */
++ color: var(--text-tertiary, #6b7280);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+@@ -2852,15 +2966,14 @@ if (
+ }
+
+ .section-tab.active {
+- color: #3b82f6 !important; /* text-blue-500 - Force blue */
++ color: var(--primary, #1e40af) !important;
+ background: transparent !important;
+ transform: none !important;
+- text-shadow: 0 0 12px rgba(59, 130, 246, 0.6); /* Text Glow */
+ }
+
+ /* Prevent hover from turning it white on mobile */
+ .section-tab.active:hover {
+- color: #3b82f6 !important;
++ color: var(--primary, #1e40af) !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+@@ -2869,7 +2982,6 @@ if (
+ opacity: 1;
+ color: currentColor;
+ transform: none;
+- filter: drop-shadow(0 0 8px rgba(59, 130, 246, 0.5)); /* Icon Glow */
+ }
+
+ .section-tab.active .tab-text {
+@@ -2918,10 +3030,10 @@ if (
+ right: 0;
+ width: 8px;
+ height: 8px;
+- background: #0b5afe;
++ background: var(--primary, #1e40af);
+ border-radius: 50%;
+- border: 1.5px solid #0a0a0f;
+- box-shadow: 0 0 5px rgba(11, 90, 254, 0.4);
++ border: 1.5px solid var(--surface-primary, var(--bg-primary));
++ box-shadow: 0 0 5px var(--primary-light, rgba(30, 64, 175, 0.08));
+ z-index: 2;
+ }
+
+@@ -2980,8 +3092,8 @@ if (
+ padding: 6px 14px;
+ font-size: 12px;
+ border-radius: 6px;
+- background: rgba(11, 90, 254, 0.15);
+- border: 1px solid rgba(11, 90, 254, 0.3);
++ background: var(--primary-light, rgba(30, 64, 175, 0.08));
++ border: 1px solid var(--primary-border, rgba(30, 64, 175, 0.15));
+ }
+ }
+
+@@ -3067,10 +3179,10 @@ if (
+
+ /* 寮圭獥鍐呭 */
+ .modal-content {
+- background: linear-gradient(135deg, #1a1b24 0%, #121318 100%);
++ background: var(--surface-primary, var(--bg-primary, #ffffff));
+ border-radius: 20px;
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
++ border: 1px solid var(--border-primary, #e5e7eb);
++ box-shadow: var(--shadow-card, 0 25px 50px -12px rgba(0, 0, 0, 0.12));
+ max-width: 420px;
+ width: 90%;
+ overflow: hidden;
+@@ -3094,7 +3206,7 @@ if (
+ justify-content: space-between;
+ align-items: center;
+ padding: 24px 28px 16px;
+- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
++ border-bottom: 1px solid var(--border-secondary, #f1f5f9);
+ }
+
+ .modal-header h2 {
+@@ -3105,7 +3217,7 @@ if (
+ sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ margin: 0;
+ letter-spacing: 0.02em;
+ }
+@@ -3115,9 +3227,9 @@ if (
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+- background: rgba(255, 255, 255, 0.08);
++ background: var(--surface-tertiary, var(--bg-tertiary, #f1f5f9));
+ border: none;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-tertiary, #64748b);
+ font-size: 24px;
+ line-height: 1;
+ cursor: pointer;
+@@ -3128,8 +3240,8 @@ if (
+ }
+
+ .close-button:hover {
+- background: rgba(255, 255, 255, 0.15);
+- color: #ffffff;
++ background: var(--border-secondary, #e2e8f0);
++ color: var(--text-primary, #1a2332);
+ transform: rotate(90deg);
+ }
+
+@@ -3156,24 +3268,24 @@ if (
+ gap: 0.5rem;
+ font-size: 16px;
+ font-weight: 700;
+- color: #ffffff;
++ color: var(--text-primary, #1a2332);
+ margin: 0;
+ }
+
+ .rules-icon {
+- color: #3b82f6;
++ color: var(--primary, #0b5afe);
+ }
+
+ .rules-text {
+ font-size: 14px;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-secondary, #4a5568);
+ line-height: 1.6;
+ margin: 0;
+ }
+
+ .guidelines-content {
+ font-size: 14px;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-secondary, #4a5568);
+ line-height: 1.6;
+ }
+
+@@ -3186,31 +3298,31 @@ if (
+ .rule-item {
+ display: flex;
+ font-size: 14px;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-secondary, #4a5568);
+ line-height: 1.5;
+ }
+
+ .rule-item span {
+ margin-right: 0.5rem;
+- color: rgba(255, 255, 255, 0.3);
++ color: var(--text-disabled, #94a3b8);
+ font-weight: 600;
+ }
+
+ /* 骞村害鎶ュ憡寮圭獥 */
+ .year-review-overlay {
+ backdrop-filter: blur(8px);
+- background: rgba(0, 0, 0, 0.4);
++ background: rgba(15, 23, 42, 0.35);
+ }
+
+ .year-review-card {
+ position: relative;
+ width: 90%;
+ max-width: 400px;
+- background: #12121a;
++ background: var(--surface-primary, var(--bg-primary, #ffffff));
+ border-radius: 32px;
+ overflow: hidden;
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
++ border: 1px solid var(--border-primary, #e5e7eb);
++ box-shadow: var(--shadow-card, 0 25px 50px -12px rgba(0, 0, 0, 0.12));
+ animation: card-appear 0.6s cubic-bezier(0.16, 1, 0.3, 1);
+ }
+
+@@ -3227,7 +3339,7 @@ if (
+ .card-pattern {
+ position: absolute;
+ inset: 0;
+- background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px);
++ background-image: radial-gradient(rgba(15, 23, 42, 0.06) 1px, transparent 1px);
+ background-size: 20px 20px;
+ opacity: 0.5;
+ }
+@@ -3239,9 +3351,9 @@ if (
+ width: 36px;
+ height: 36px;
+ border-radius: 12px;
+- background: rgba(255, 255, 255, 0.05);
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- color: rgba(255, 255, 255, 0.4);
++ background: var(--surface-tertiary, var(--bg-tertiary, #f1f5f9));
++ border: 1px solid var(--border-secondary, #f1f5f9);
++ color: var(--text-tertiary, #64748b);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+@@ -3251,8 +3363,8 @@ if (
+ }
+
+ .card-close:hover {
+- background: rgba(255, 255, 255, 0.1);
+- color: #fff;
++ background: var(--border-secondary, #e2e8f0);
++ color: var(--text-primary, #1a2332);
+ transform: translateY(2px);
+ }
+
+@@ -3267,9 +3379,9 @@ if (
+ display: inline-block;
+ padding: 4px 12px;
+ background: rgba(139, 92, 246, 0.1);
+- border: 1px solid rgba(139, 92, 246, 0.2);
++ border: 1px solid rgba(139, 92, 246, 0.25);
+ border-radius: 99px;
+- color: #a78bfa;
++ color: #7c3aed;
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+@@ -3293,8 +3405,8 @@ if (
+ display: flex;
+ align-items: center;
+ justify-content: center;
+- color: #fff;
+- box-shadow: 0 10px 25px -5px rgba(139, 92, 246, 0.5);
++ color: var(--text-primary, #ffffff);
++ box-shadow: 0 10px 25px -5px rgba(139, 92, 246, 0.35);
+ z-index: 2;
+ transform: rotate(-5deg);
+ }
+@@ -3349,14 +3461,14 @@ if (
+ .card-title {
+ font-size: 24px;
+ font-weight: 800;
+- color: #fff;
++ color: var(--text-primary, #1a2332);
+ margin-bottom: 12px;
+ letter-spacing: -0.01em;
+ }
+
+ .card-description {
+ font-size: 15px;
+- color: rgba(255, 255, 255, 0.5);
++ color: var(--text-secondary, #4a5568);
+ line-height: 1.6;
+ margin-bottom: 32px;
+ }
+@@ -3370,10 +3482,10 @@ if (
+ .btn-primary {
+ width: 100%;
+ padding: 16px;
+- background: #fff;
++ background: var(--primary, #0b5afe);
+ border: none;
+ border-radius: 16px;
+- color: #000;
++ color: var(--text-primary, #ffffff);
+ font-size: 16px;
+ font-weight: 700;
+ display: flex;
+@@ -3386,8 +3498,8 @@ if (
+
+ .btn-primary:hover {
+ transform: translateY(-2px);
+- box-shadow: 0 10px 20px rgba(255, 255, 255, 0.1);
+- background: #f4f4f4;
++ box-shadow: 0 10px 20px rgba(11, 90, 254, 0.2);
++ background: var(--primary-hover, #0043f8);
+ }
+
+ .btn-primary:active {
+@@ -3398,9 +3510,9 @@ if (
+ width: 100%;
+ padding: 14px;
+ background: transparent;
+- border: 1px solid rgba(255, 255, 255, 0.1);
++ border: 1px solid var(--border-primary, #e5e7eb);
+ border-radius: 16px;
+- color: rgba(255, 255, 255, 0.6);
++ color: var(--text-secondary, #4a5568);
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+@@ -3408,9 +3520,9 @@ if (
+ }
+
+ .btn-secondary:hover {
+- background: rgba(255, 255, 255, 0.05);
+- color: #fff;
+- border-color: rgba(255, 255, 255, 0.2);
++ background: var(--surface-tertiary, var(--bg-tertiary, #f1f5f9));
++ color: var(--text-primary, #1a2332);
++ border-color: var(--border-secondary, #d1d5db);
+ }
+
+ @keyframes card-appear {
+@@ -3452,7 +3564,7 @@ if (
+ .ripple-effect {
+ position: absolute;
+ border-radius: 50%;
+- background-color: rgba(255, 255, 255, 0.3);
++ background-color: rgba(11, 90, 254, 0.25);
+ transform: scale(0);
+ animation: ripple 0.6s linear;
+ pointer-events: none;
+@@ -3488,7 +3600,7 @@ if (
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+- background-color: #0b5afe;
++ background-color: var(--bg-secondary, #0b5afe);
+ display: inline-block;
+ z-index: 2;
+ }
+@@ -3528,32 +3640,33 @@ if (
+ text-align: center;
+ max-width: 400px;
+ padding: 30px;
+- background-color: rgba(30, 41, 59, 0.5);
++ background-color: var(--surface-secondary, var(--bg-secondary, #f7fafc));
+ border-radius: 12px;
+- border: 1px solid rgba(255, 255, 255, 0.1);
+- box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
++ border: 1px solid var(--border-primary, #e5e7eb);
++ box-shadow: var(--shadow-card, 0 5px 20px rgba(0, 0, 0, 0.08));
+ }
+
+ .login-icon {
+ font-size: 3rem;
+ margin-bottom: 15px;
++ color: var(--primary, #0b5afe);
+ }
+
+ .login-required-content h3 {
+ font-size: 1.5rem;
+ margin-bottom: 10px;
+- color: var(--light);
++ color: var(--text-primary, #1a2332);
+ }
+
+ .login-required-content p {
+ margin-bottom: 20px;
+- color: rgba(255, 255, 255, 0.7);
++ color: var(--text-secondary, #4a5568);
+ }
+
+ .login-button {
+- background: linear-gradient(180deg, #0043f8 0%, #0075f8 100%);
++ background: linear-gradient(180deg, var(--bg-secondary, #0043f8) 0%, #0075f8 100%);
+ border: none;
+- color: white;
++ color: var(--text-primary, #ffffff);
+ padding: 10px 20px;
+ border-radius: 6px;
+ font-weight: 600;
+diff --git a/app/pages/login.vue b/app/pages/login.vue
+index f310bad..0ab59a2 100644
+--- a/app/pages/login.vue
++++ b/app/pages/login.vue
+@@ -312,7 +312,7 @@ onMounted(async () => {
+
+ .icp-link:hover,
+ .voicehub-link:hover {
+- color: rgba(255, 255, 255, 0.8);
++ color: var(--text-secondary);
+ }
+
+ @media (max-width: 768px) {
+diff --git a/app/pages/year-review.vue b/app/pages/year-review.vue
+index aae3219..398dd2c 100644
+--- a/app/pages/year-review.vue
++++ b/app/pages/year-review.vue
+@@ -211,8 +211,8 @@ const handleTouchEnd = (e) => {
+ .review-page {
+ position: fixed;
+ inset: 0;
+- background-color: #000;
+- color: #fff;
++ background-color: var(--bg-primary, #111111);
++ color: var(--text-primary, #ffffff);
+ z-index: 9999;
+ font-family:
+ 'MiSans',
+@@ -230,7 +230,7 @@ const handleTouchEnd = (e) => {
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+- background-color: #000;
++ background-color: var(--bg-primary, #111111);
+ z-index: 50;
+ }
+
+@@ -285,7 +285,7 @@ const handleTouchEnd = (e) => {
+ .empty .empty-icon-circle {
+ width: 5rem;
+ height: 5rem;
+- background: #111827;
++ background: var(--bg-secondary, #111827);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+@@ -364,7 +364,7 @@ const handleTouchEnd = (e) => {
+
+ .indicator-dot.active {
+ height: 1.5rem;
+- background: #fff;
++ background: var(--card-bg, #fff);
+ }
+
+ /* 婊氬姩鎻愮ず */
+diff --git a/app/plugins/sentry.client.ts b/app/plugins/sentry.client.ts
+new file mode 100644
+index 0000000..5217478
+--- /dev/null
++++ b/app/plugins/sentry.client.ts
+@@ -0,0 +1,29 @@
++import * as Sentry from '@sentry/vue'
++
++export default defineNuxtPlugin((nuxtApp) => {
++ const config = useRuntimeConfig()
++ const dsn = config.public.sentry?.dsn
++
++ if (!dsn) return
++
++ const app = nuxtApp.vueApp
++ const router = nuxtApp.$router
++
++ Sentry.init({
++ app,
++ dsn,
++ environment: config.sentry?.environment || 'production',
++ release: config.sentry?.release || undefined,
++ tracesSampleRate: config.public.sentry?.tracesSampleRate ?? 1.0,
++ integrations: router
++ ? [
++ Sentry.browserTracingIntegration({ router }),
++ Sentry.replayIntegration()
++ ]
++ : [
++ Sentry.replayIntegration()
++ ],
++ replaysSessionSampleRate: config.public.sentry?.replaySessionSampleRate ?? 0,
++ replaysOnErrorSampleRate: config.public.sentry?.replayOnErrorSampleRate ?? 0
++ })
++})
+diff --git a/app/utils/password-policy.ts b/app/utils/password-policy.ts
+new file mode 100644
+index 0000000..f84c3d4
+--- /dev/null
++++ b/app/utils/password-policy.ts
+@@ -0,0 +1,2 @@
++export const PASSWORD_MIN_LENGTH = 6
++export const PASSWORD_MAX_LENGTH = 64
+diff --git a/docker-compose.yml b/docker-compose.yml
+index c1ed74d..abb81bc 100644
+--- a/docker-compose.yml
++++ b/docker-compose.yml
+@@ -1,57 +1,162 @@
++# 鈿狅笍 Docker Compose v2 椤剁骇 name 蹇呴』鍖归厤姝e垯 ^[a-z0-9][a-z0-9_-]*$
++# 涓嶈兘鏈夊ぇ鍐欍€佺┖鏍笺€佹嫭鍙枫€佷腑鏂囷紝鍚﹀垯浼氭姤 name Does not match pattern 閿欒
++name: byw-voicehub
+ services:
++ # ========================================================================
++ # 搴旂敤鏈綋 - Nuxt 4 (Node SSR)
++ # - 浣跨敤涓ら樁娈垫瀯寤?Dockerfile锛屾瀯寤洪樁娈靛唴瀛樺弸濂?+ # - PREBUILT=true锛氬憡璇?scripts/deploy.js 璺宠繃 npm install 鍜?nuxt build
++ # 锛堝洜涓虹涓€闃舵宸茬粡 build 濂戒簡锛屼骇鐗╁湪 .output/ 閲岋級
++ # ========================================================================
+ voicehub:
+- build: .
++ image: byw-voicehub:1.7.0.0
++ build:
++ context: .
++ dockerfile: Dockerfile
++ args:
++ BUILDKIT_INLINE_CACHE: "1"
++ container_name: voicehub-app
++ restart: unless-stopped
+ ports:
+- - '4456:3000'
+- environment:
+- - DATABASE_URL=postgresql://user:password@postgres:5432/voicehub
+- - JWT_SECRET=your-jwt-secret-here
+- - NODE_ENV=production
+- - NODE_OPTIONS=--max-old-space-size=512
+- - SKIP_BUILD=true
+- mem_limit: 1200m
+- mem_reservation: 256m
+- cpus: '1.0'
+- deploy:
+- resources:
+- limits:
+- cpus: '1'
+- memory: 1200M
+- reservations:
+- cpus: '0.25'
+- memory: 256M
++ # 宸﹁竟鏄涓绘満绔彛锛堝澶栵級锛屽彸杈规槸瀹瑰櫒鍐呯鍙o紙鍥哄畾 3000锛?+ # 濡傛灉瑕佸 Nginx锛屽氨鍙洃鍚?127.0.0.1:4456:3000 涓嶇洿鎺ユ毚闇插缃?+ - "4456:3000"
+ depends_on:
+ postgres:
+ condition: service_healthy
+- restart: unless-stopped
+-
+- postgres:
+- image: postgres:15-alpine
++ redis:
++ condition: service_healthy
++ env_file:
++ # 鎶婂瘑鐮併€佸瘑閽ラ兘鍐欏湪椤圭洰鏍圭殑 .env 閲岋紝涓嶈鐩存帴鍐欒繘杩欎釜 yaml
++ - .env
+ environment:
+- - POSTGRES_USER=user
+- - POSTGRES_PASSWORD=password
+- - POSTGRES_DB=voicehub
+- volumes:
+- - postgres_data:/var/lib/postgresql/data
+- ports:
+- - '5433:5432'
+- mem_limit: 1200m
+- mem_reservation: 256m
+- cpus: '1.0'
++ # ===== 杩愯鏃跺熀纭€锛堝彲浠ヨ .env 瑕嗙洊锛?=====
++ NODE_ENV: production
++ PORT: 3000
++ HOST: 0.0.0.0
++ PREBUILT: "true"
++ SKIP_INSTALL: "true"
++ SKIP_BUILD: "true"
++ NITRO_PRESET: node-server
++
++ # ===== 鏁版嵁搴撹繛鎺ヤ覆锛?env 鍐欎簡灏辩敤 .env锛屾病鍐欏氨鑷姩鎷?compose 鍐呴儴 postgres 鏈嶅姟锛?=====
++ DATABASE_URL: ${DATABASE_URL:-postgresql://${POSTGRES_USER:-voicehub}:${POSTGRES_PASSWORD:-voicehub}@postgres:5432/${POSTGRES_DB:-voicehub}?sslmode=disable}
++
++ # ===== Redis 杩炴帴涓诧紙鍚屼笂锛岃嚜鍔ㄦ嫾鍐呴儴 redis 鏈嶅姟锛?=====
++ REDIS_URL: ${REDIS_URL:-redis://${REDIS_PASSWORD:+:${REDIS_PASSWORD}@}redis:6379/0}
++
++ # ===== 浼氳瘽/Token锛堝繀椤诲湪 .env 閲岃锛岃繖閲岀粰涓€涓崰浣嶆彁閱掞級 =====
++ JWT_SECRET: ${JWT_SECRET:-璇峰湪.env閲岃缃甁WT_SECRET鐢╫penssl rand -hex 32鐢熸垚}
++
++ # ===== 搴旂敤榛樿锛堝彲鍦?.env 瑕嗙洊锛屼笉浼氳鐩?.env 閲屽悓鍚嶅瓧娈碉級 =====
++ NUXT_PUBLIC_SITE_URL: ${NUXT_PUBLIC_SITE_URL:-http://127.0.0.1:4456}
++ NUXT_PUBLIC_SITE_TITLE: ${NUXT_PUBLIC_SITE_TITLE:-鏍″洯骞挎挱绔欑偣姝岀郴缁焳
++ NUXT_PUBLIC_DEFAULT_THEME: ${NUXT_PUBLIC_DEFAULT_THEME:-dark}
++ NUXT_PUBLIC_ENABLED_THEMES: ${NUXT_PUBLIC_ENABLED_THEMES:-dark,light}
++
++ # ===== 鏃ュ織/鎬ц兘 =====
++ NODE_OPTIONS: ${NODE_OPTIONS:-"--max-old-space-size=768"}
++
++ # 2C4G 鏈哄櫒鐨勬帹鑽愰厤缃紙.env 鍙鐩栵級锛涘皬鏈哄櫒 1C2G 涔熻兘璺?+ # 鈿狅笍 鍏抽敭锛氱敤 deploy.resources.{limits|reservations} 杩欎竴濂楋紝
++ # 涓嶈鍚屾椂鍦ㄩ《灞傚啓 cpus/mem_limit/mem_reservation锛?+ # Docker Compose v2 绂佹涓ゅ璧勬簮瀛楁骞跺瓨锛屼細鎶?invalid compose project銆? deploy:
+ resources:
+ limits:
+- cpus: '1'
+- memory: 1200M
++ cpus: ${APP_CPUS_LIMIT:-2}
++ memory: ${APP_MEM_LIMIT:-1400M}
+ reservations:
+- cpus: '0.25'
+- memory: 256M
++ cpus: ${APP_CPUS_RESERVATION:-0.5}
++ memory: ${APP_MEM_RESERVATION:-384M}
++ healthcheck:
++ # 30s 鍚庡紑濮嬫鏌ワ紝姣?15s 涓€娆★紝杩炵画 3 娆″け璐ュ氨閲嶅惎
++ test:
++ [
++ "CMD",
++ "node",
++ "-e",
++ "fetch('http://127.0.0.1:3000/').then(r=>{process.exit(r.ok?0:1)}).catch(()=>process.exit(1))"
++ ]
++ interval: 15s
++ timeout: 5s
++ retries: 3
++ start_period: 30s
++ networks:
++ - voicehub-net
++ volumes:
++ # 锛堝彲閫夛級鎶婅嚜瀹氫箟 logo/涓婁紶璧勬簮鎸傚埌瀹夸富鏈?+ - voicehub-uploads:/app/app/public/uploads:rw
++
++ # ========================================================================
++ # PostgreSQL 16 - 涓绘暟鎹簱
++ # ========================================================================
++ postgres:
++ image: postgres:16-alpine
++ container_name: voicehub-postgres
++ restart: unless-stopped
++ environment:
++ POSTGRES_USER: ${POSTGRES_USER:-voicehub}
++ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-璇峰湪.env閲屾敼杩欎釜瀵嗙爜}
++ POSTGRES_DB: ${POSTGRES_DB:-voicehub}
++ PGDATA: /var/lib/postgresql/data/pgdata
++ # .env 閲屾病璁?DATABASE_URL 鏃讹紝鑷姩鎷间竴浠界粰 app 鐢?+ # 锛堝鏋?.env 閲屽凡缁忓啓浜嗗閮?Postgres锛屽氨浠?.env 涓哄噯锛? healthcheck:
+- test: ['CMD-SHELL', 'pg_isready -U user -d voicehub']
++ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-voicehub} -d ${POSTGRES_DB:-voicehub}"]
+ interval: 5s
+ timeout: 5s
+- retries: 5
++ retries: 10
++ volumes:
++ - postgres-data:/var/lib/postgresql/data
++ # 濡傞渶浠庡涓绘満鐢?Navicat 杩烇紝鍙栨秷涓嬩竴琛屾敞閲?+ # ports:
++ # - "127.0.0.1:5433:5432"
++ mem_limit: ${PG_MEM_LIMIT:-1200m}
++ mem_reservation: ${PG_MEM_RESERVATION:-256m}
++ cpus: ${PG_CPUS:-"1.0"}
++ networks:
++ - voicehub-net
++
++ # ========================================================================
++ # Redis 7 - 缂撳瓨 / 浼氳瘽 / 闄愭祦 / 闃熷垪
++ # ========================================================================
++ redis:
++ image: redis:7-alpine
++ container_name: voicehub-redis
+ restart: unless-stopped
++ command: >
++ sh -c "
++ if [ -n \"$$REDIS_PASSWORD\" ]; then
++ redis-server --appendonly yes --requirepass \"$$REDIS_PASSWORD\"
++ else
++ redis-server --appendonly yes
++ fi
++ "
++ environment:
++ REDIS_PASSWORD: ${REDIS_PASSWORD:-}
++ healthcheck:
++ test: ["CMD", "redis-cli", "ping"]
++ interval: 5s
++ timeout: 3s
++ retries: 10
++ volumes:
++ - redis-data:/data
++ mem_limit: ${REDIS_MEM_LIMIT:-384m}
++ mem_reservation: ${REDIS_MEM_RESERVATION:-64m}
++ cpus: ${REDIS_CPUS:-0.5}
++ networks:
++ - voicehub-net
+
+ volumes:
+- postgres_data:
++ postgres-data:
++ name: voicehub-postgres-data
++ redis-data:
++ name: voicehub-redis-data
++ voicehub-uploads:
++ name: voicehub-uploads
++
++networks:
++ voicehub-net:
++ name: voicehub-net
++ driver: bridge
+diff --git a/nuxt.config.ts b/nuxt.config.ts
+index a141ae2..d5ff5f3 100644
+--- a/nuxt.config.ts
++++ b/nuxt.config.ts
+@@ -40,11 +40,51 @@ export default defineNuxtConfig({
+ },
+ modules: [
+ '@nuxtjs/tailwindcss',
+- ...(process.env.NODE_ENV === 'development' || process.env.npm_lifecycle_event?.includes('lint')
++ ...((process.env.NODE_ENV === 'development' || process.env.npm_lifecycle_event?.includes('lint'))
+ ? ['@nuxt/eslint']
++ : []),
++ ...((process.env.PWA_ENABLED || 'false') === 'true'
++ ? ['@vite-pwa/nuxt']
+ : [])
+ ],
+
++ pwa: (process.env.PWA_ENABLED || 'false') === 'true'
++ ? {
++ registerType: 'autoUpdate',
++ manifest: {
++ name: process.env.NUXT_PUBLIC_SITE_TITLE || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
++ short_name: process.env.NUXT_PUBLIC_SITE_TITLE || 'VoiceHub',
++ description: process.env.NUXT_PUBLIC_SITE_DESCRIPTION || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
++ display: (process.env.PWA_DISPLAY as any) || 'standalone',
++ background_color: process.env.PWA_BACKGROUND_COLOR || '#000000',
++ theme_color: process.env.PWA_THEME_COLOR || '#111111',
++ icons: [
++ {
++ src: '/images/logo.png',
++ sizes: '192x192',
++ type: 'image/png'
++ },
++ {
++ src: '/images/logo.png',
++ sizes: '512x512',
++ type: 'image/png'
++ }
++ ]
++ },
++ workbox: {
++ navigateFallback: '/',
++ globPatterns: ['**/*.{js,css,html,png,jpg,jpeg,svg,ico,webp,woff2}']
++ },
++ client: {
++ installPrompt: true,
++ periodicSyncForUpdates: 3600
++ },
++ devOptions: {
++ enabled: process.env.NODE_ENV === 'development'
++ }
++ }
++ : {},
++
+ // 寮曞叆鍏ㄥ眬CSS
+ css: [
+ '~/assets/css/variables.css',
+@@ -116,8 +156,8 @@ export default defineNuxtConfig({
+ themeColor: process.env.PWA_THEME_COLOR || '#111111'
+ },
+ // 涓婚閰嶇疆
+- defaultTheme: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'dark',
+- enabledThemes: (process.env.NUXT_PUBLIC_ENABLED_THEMES || 'dark,light')
++ defaultTheme: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'ClassicLight',
++ enabledThemes: (process.env.NUXT_PUBLIC_ENABLED_THEMES || 'ClassicLight,ModernLight')
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean),
+@@ -150,6 +190,10 @@ export default defineNuxtConfig({
+ },
+ // 绉诲姩绔紭鍖? { name: 'theme-color', content: '#111111' },
++ {
++ name: 'x-default-theme',
++ content: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'ClassicLight'
++ },
+ { name: 'apple-mobile-web-app-capable', content: 'yes' },
+ { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
+ { name: 'apple-mobile-web-app-title', content: 'VoiceHub绠$悊' },
+@@ -162,22 +206,25 @@ export default defineNuxtConfig({
+ tagPosition: 'head',
+ children: [
+ `(function(){try{`,
+- ` var k='voicehub:theme', d=document, r=d.documentElement, s=null;`,
++ ` var k='voicehub:theme', d=document, r=d.documentElement, s=null, eff=null;`,
+ ` try{s=localStorage.getItem(k)}catch(e){s=null}`,
+ ` if(!s){`,
+ ` try{`,
+ ` var meta=document.querySelector('meta[name="x-default-theme"]');`,
+ ` if(meta && meta.content){s=meta.content}`,
+ ` }catch(e){s=null}`,
+- ` if(!s){s=window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}`,
++ ` if(!s){s='ClassicLight'}`,
+ ` }`,
+- ` r.setAttribute('data-theme', s);`,
+- ` if(s==='dark'){r.classList.add('dark'); r.style.colorScheme='dark'}`,
+- ` else if(s==='light'){r.classList.add('light'); r.style.colorScheme='light'}`,
+- ` else{r.style.colorScheme='light dark'}`,
++ ` if(s==='System'||s==='system'){eff=window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}`,
++ ` else if(typeof s==='string'){eff=s.endsWith('Dark')||s==='dark'?'dark':(s.endsWith('Light')||s==='light'?'light':s)}`,
++ ` else{eff='dark'}`,
++ ` r.setAttribute('data-theme', (typeof s==='string'?s:eff));`,
++ ` if(eff==='dark'){r.classList.add('dark'); r.classList.remove('light'); r.style.colorScheme='dark'}`,
++ ` else if(eff==='light'){r.classList.add('light'); r.classList.remove('dark'); r.style.colorScheme='light'}`,
++ ` else{r.classList.remove('dark','light'); r.style.colorScheme='light dark'}`,
+ ` try{`,
+ ` var tm=d.querySelector('meta[name="theme-color"]');`,
+- ` if(tm){tm.content=(s==='light'?'#f8fafc':'#111111')}`,
++ ` if(tm){tm.content=(eff==='light'?'#f8fafc':'#111111')}`,
+ ` }catch(e){}`,
+ `}catch(e){}})();`
+ ].join('')
+diff --git a/package-lock.json b/package-lock.json
+index 087f52b..5866f60 100644
+--- a/package-lock.json
++++ b/package-lock.json
+@@ -25,7 +25,8 @@
+ "@pixi/filter-bulge-pinch": "^5.1.1",
+ "@pixi/filter-color-matrix": "^7.4.3",
+ "@pixi/sprite": "^7.4.3",
+- "@sansenjian/qq-music-api": "2.6.0",
++ "@sentry/node": "^10.73.0",
++ "@sentry/vue": "^10.73.0",
+ "@simplewebauthn/browser": "^13.3.0",
+ "@simplewebauthn/server": "^13.3.3",
+ "@types/crypto-js": "^4.2.2",
+@@ -36,6 +37,7 @@
+ "@types/pg": "^8.20.0",
+ "@ungap/structured-clone": "^1.3.0",
+ "@vercel/speed-insights": "^1.3.1",
++ "@vite-pwa/nuxt": "^1.1.1",
+ "axios": "^1.20.0",
+ "bcryptjs": "^2.4.3",
+ "bezier-easing": "^2.1.0",
+@@ -78,6 +80,7 @@
+ "zod": "^4.5.4"
+ },
+ "devDependencies": {
++ "@clack/prompts": "^1.1.0",
+ "@nuxt/eslint": "^1.17.0",
+ "eslint": "^9.39.5",
+ "prettier": "^3.9.6",
+@@ -421,7 +424,6 @@
+ "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+@@ -539,6 +541,48 @@
+ "semver": "bin/semver.js"
+ }
+ },
++ "node_modules/@babel/helper-create-regexp-features-plugin": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz",
++ "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-annotate-as-pure": "^7.29.7",
++ "regexpu-core": "^6.3.1",
++ "semver": "^6.3.1"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
++ "version": "6.3.1",
++ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
++ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
++ "license": "ISC",
++ "bin": {
++ "semver": "bin/semver.js"
++ }
++ },
++ "node_modules/@babel/helper-define-polyfill-provider": {
++ "version": "0.6.8",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
++ "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-compilation-targets": "^7.28.6",
++ "@babel/helper-plugin-utils": "^7.28.6",
++ "debug": "^4.4.3",
++ "lodash.debounce": "^4.0.8",
++ "resolve": "^1.22.11"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
++ }
++ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+@@ -574,15 +618,986 @@
+ "node": ">=6.9.0"
+ }
+ },
+- "node_modules/@babel/helper-module-transforms": {
++ "node_modules/@babel/helper-module-transforms": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
++ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-imports": "^7.29.7",
++ "@babel/helper-validator-identifier": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/helper-optimise-call-expression": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz",
++ "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/types": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-plugin-utils": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
++ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-remap-async-to-generator": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz",
++ "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-annotate-as-pure": "^7.29.7",
++ "@babel/helper-wrap-function": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/helper-replace-supers": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz",
++ "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-member-expression-to-functions": "^7.29.7",
++ "@babel/helper-optimise-call-expression": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz",
++ "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/traverse": "^7.29.7",
++ "@babel/types": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-string-parser": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
++ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-validator-identifier": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
++ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-validator-option": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
++ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helper-wrap-function": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz",
++ "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/template": "^7.29.7",
++ "@babel/traverse": "^7.29.7",
++ "@babel/types": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/helpers": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz",
++ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/template": "^7.29.7",
++ "@babel/types": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ }
++ },
++ "node_modules/@babel/parser": {
++ "version": "7.29.8",
++ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz",
++ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/types": "^7.29.8"
++ },
++ "bin": {
++ "parser": "bin/babel-parser.js"
++ },
++ "engines": {
++ "node": ">=6.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz",
++ "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz",
++ "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz",
++ "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz",
++ "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz",
++ "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
++ "@babel/plugin-transform-optional-chaining": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.13.0"
++ }
++ },
++ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz",
++ "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-proposal-private-property-in-object": {
++ "version": "7.21.0-placeholder-for-preset-env.2",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
++ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-syntax-import-assertions": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz",
++ "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-syntax-import-attributes": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz",
++ "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-syntax-jsx": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz",
++ "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-syntax-typescript": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz",
++ "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
++ "version": "7.18.6",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
++ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
++ "@babel/helper-plugin-utils": "^7.18.6"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-arrow-functions": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
++ "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-async-generator-functions": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz",
++ "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-remap-async-to-generator": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-async-to-generator": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz",
++ "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-imports": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-remap-async-to-generator": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-block-scoped-functions": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz",
++ "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-block-scoping": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz",
++ "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-class-properties": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz",
++ "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-class-static-block": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz",
++ "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.12.0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-classes": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz",
++ "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-annotate-as-pure": "^7.29.7",
++ "@babel/helper-compilation-targets": "^7.29.7",
++ "@babel/helper-globals": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-replace-supers": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-computed-properties": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz",
++ "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/template": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-destructuring": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz",
++ "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-dotall-regex": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz",
++ "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-duplicate-keys": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz",
++ "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz",
++ "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-dynamic-import": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz",
++ "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-explicit-resource-management": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz",
++ "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/plugin-transform-destructuring": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-exponentiation-operator": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz",
++ "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-export-namespace-from": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz",
++ "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-for-of": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz",
++ "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-function-name": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz",
++ "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-compilation-targets": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-json-strings": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz",
++ "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-literals": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz",
++ "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz",
++ "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-member-expression-literals": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz",
++ "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-modules-amd": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz",
++ "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-transforms": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-modules-commonjs": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz",
++ "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-transforms": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-modules-systemjs": {
++ "version": "7.29.8",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz",
++ "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-transforms": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-validator-identifier": "^7.29.7",
++ "@babel/traverse": "^7.29.8"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-modules-umd": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz",
++ "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-transforms": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz",
++ "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-new-target": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz",
++ "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz",
++ "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-numeric-separator": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz",
++ "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-object-rest-spread": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz",
++ "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-compilation-targets": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/plugin-transform-destructuring": "^7.29.7",
++ "@babel/plugin-transform-parameters": "^7.29.7",
++ "@babel/traverse": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-object-super": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz",
++ "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-replace-supers": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-optional-catch-binding": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz",
++ "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-optional-chaining": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz",
++ "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-parameters": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz",
++ "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-private-methods": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz",
++ "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-private-property-in-object": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz",
++ "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-annotate-as-pure": "^7.29.7",
++ "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-property-literals": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz",
++ "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-regenerator": {
++ "version": "7.29.8",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz",
++ "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
++ "engines": {
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
++ }
++ },
++ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+- "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz",
++ "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/helper-module-imports": "^7.29.7",
+- "@babel/helper-validator-identifier": "^7.29.7",
+- "@babel/traverse": "^7.29.7"
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+@@ -591,118 +1606,154 @@
+ "@babel/core": "^7.0.0"
+ }
+ },
+- "node_modules/@babel/helper-optimise-call-expression": {
++ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz",
+- "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz",
++ "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/types": "^7.29.7"
++ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-plugin-utils": {
++ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+- "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz",
++ "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==",
+ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-replace-supers": {
+- "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz",
+- "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==",
++ "node_modules/@babel/plugin-transform-spread": {
++ "version": "7.29.8",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz",
++ "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/helper-member-expression-to-functions": "^7.29.7",
+- "@babel/helper-optimise-call-expression": "^7.29.7",
+- "@babel/traverse": "^7.29.7"
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+- "@babel/core": "^7.0.0"
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
++ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz",
+- "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz",
++ "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/traverse": "^7.29.7",
+- "@babel/types": "^7.29.7"
++ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-string-parser": {
++ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+- "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz",
++ "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==",
+ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-validator-identifier": {
++ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+- "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz",
++ "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==",
+ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.29.7"
++ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helper-validator-option": {
++ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+- "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz",
++ "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==",
+ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-annotate-as-pure": "^7.29.7",
++ "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7",
++ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
++ "@babel/plugin-syntax-typescript": "^7.29.7"
++ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/helpers": {
++ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz",
+- "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz",
++ "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/template": "^7.29.7",
+- "@babel/types": "^7.29.7"
++ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/parser": {
+- "version": "7.29.8",
+- "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz",
+- "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
++ "node_modules/@babel/plugin-transform-unicode-property-regex": {
++ "version": "7.29.7",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz",
++ "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/types": "^7.29.8"
+- },
+- "bin": {
+- "parser": "bin/babel-parser.js"
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
++ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+- "node": ">=6.0.0"
++ "node": ">=6.9.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/plugin-syntax-jsx": {
++ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz",
+- "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz",
++ "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==",
+ "license": "MIT",
+ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+@@ -712,33 +1763,99 @@
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+- "node_modules/@babel/plugin-syntax-typescript": {
++ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz",
+- "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==",
++ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz",
++ "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
++ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+- "@babel/core": "^7.0.0-0"
++ "@babel/core": "^7.0.0"
+ }
+ },
+- "node_modules/@babel/plugin-transform-typescript": {
++ "node_modules/@babel/preset-env": {
+ "version": "7.29.7",
+- "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz",
+- "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==",
++ "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.7.tgz",
++ "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==",
+ "license": "MIT",
+ "dependencies": {
+- "@babel/helper-annotate-as-pure": "^7.29.7",
+- "@babel/helper-create-class-features-plugin": "^7.29.7",
++ "@babel/compat-data": "^7.29.7",
++ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+- "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
+- "@babel/plugin-syntax-typescript": "^7.29.7"
++ "@babel/helper-validator-option": "^7.29.7",
++ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7",
++ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7",
++ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7",
++ "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7",
++ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7",
++ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7",
++ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
++ "@babel/plugin-syntax-import-assertions": "^7.29.7",
++ "@babel/plugin-syntax-import-attributes": "^7.29.7",
++ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
++ "@babel/plugin-transform-arrow-functions": "^7.29.7",
++ "@babel/plugin-transform-async-generator-functions": "^7.29.7",
++ "@babel/plugin-transform-async-to-generator": "^7.29.7",
++ "@babel/plugin-transform-block-scoped-functions": "^7.29.7",
++ "@babel/plugin-transform-block-scoping": "^7.29.7",
++ "@babel/plugin-transform-class-properties": "^7.29.7",
++ "@babel/plugin-transform-class-static-block": "^7.29.7",
++ "@babel/plugin-transform-classes": "^7.29.7",
++ "@babel/plugin-transform-computed-properties": "^7.29.7",
++ "@babel/plugin-transform-destructuring": "^7.29.7",
++ "@babel/plugin-transform-dotall-regex": "^7.29.7",
++ "@babel/plugin-transform-duplicate-keys": "^7.29.7",
++ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7",
++ "@babel/plugin-transform-dynamic-import": "^7.29.7",
++ "@babel/plugin-transform-explicit-resource-management": "^7.29.7",
++ "@babel/plugin-transform-exponentiation-operator": "^7.29.7",
++ "@babel/plugin-transform-export-namespace-from": "^7.29.7",
++ "@babel/plugin-transform-for-of": "^7.29.7",
++ "@babel/plugin-transform-function-name": "^7.29.7",
++ "@babel/plugin-transform-json-strings": "^7.29.7",
++ "@babel/plugin-transform-literals": "^7.29.7",
++ "@babel/plugin-transform-logical-assignment-operators": "^7.29.7",
++ "@babel/plugin-transform-member-expression-literals": "^7.29.7",
++ "@babel/plugin-transform-modules-amd": "^7.29.7",
++ "@babel/plugin-transform-modules-commonjs": "^7.29.7",
++ "@babel/plugin-transform-modules-systemjs": "^7.29.7",
++ "@babel/plugin-transform-modules-umd": "^7.29.7",
++ "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7",
++ "@babel/plugin-transform-new-target": "^7.29.7",
++ "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7",
++ "@babel/plugin-transform-numeric-separator": "^7.29.7",
++ "@babel/plugin-transform-object-rest-spread": "^7.29.7",
++ "@babel/plugin-transform-object-super": "^7.29.7",
++ "@babel/plugin-transform-optional-catch-binding": "^7.29.7",
++ "@babel/plugin-transform-optional-chaining": "^7.29.7",
++ "@babel/plugin-transform-parameters": "^7.29.7",
++ "@babel/plugin-transform-private-methods": "^7.29.7",
++ "@babel/plugin-transform-private-property-in-object": "^7.29.7",
++ "@babel/plugin-transform-property-literals": "^7.29.7",
++ "@babel/plugin-transform-regenerator": "^7.29.7",
++ "@babel/plugin-transform-regexp-modifiers": "^7.29.7",
++ "@babel/plugin-transform-reserved-words": "^7.29.7",
++ "@babel/plugin-transform-shorthand-properties": "^7.29.7",
++ "@babel/plugin-transform-spread": "^7.29.7",
++ "@babel/plugin-transform-sticky-regex": "^7.29.7",
++ "@babel/plugin-transform-template-literals": "^7.29.7",
++ "@babel/plugin-transform-typeof-symbol": "^7.29.7",
++ "@babel/plugin-transform-unicode-escapes": "^7.29.7",
++ "@babel/plugin-transform-unicode-property-regex": "^7.29.7",
++ "@babel/plugin-transform-unicode-regex": "^7.29.7",
++ "@babel/plugin-transform-unicode-sets-regex": "^7.29.7",
++ "@babel/preset-modules": "0.1.6-no-external-plugins",
++ "babel-plugin-polyfill-corejs2": "^0.4.15",
++ "babel-plugin-polyfill-corejs3": "^0.14.0",
++ "babel-plugin-polyfill-regenerator": "^0.6.6",
++ "core-js-compat": "^3.48.0",
++ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+@@ -747,6 +1864,29 @@
+ "@babel/core": "^7.0.0-0"
+ }
+ },
++ "node_modules/@babel/preset-env/node_modules/semver": {
++ "version": "6.3.1",
++ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
++ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
++ "license": "ISC",
++ "bin": {
++ "semver": "bin/semver.js"
++ }
++ },
++ "node_modules/@babel/preset-modules": {
++ "version": "0.1.6-no-external-plugins",
++ "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
++ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-plugin-utils": "^7.0.0",
++ "@babel/types": "^7.4.4",
++ "esutils": "^2.0.2"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
++ }
++ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+@@ -808,31 +1948,24 @@
+ "license": "LGPL-3.0"
+ },
+ "node_modules/@clack/core": {
+- "version": "1.4.3",
+- "resolved": "https://registry.npmmirror.com/@clack/core/-/core-1.4.3.tgz",
+- "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==",
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/@clack/core/-/core-1.1.0.tgz",
++ "integrity": "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==",
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+- "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+- },
+- "engines": {
+- "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@clack/prompts": {
+- "version": "1.7.0",
+- "resolved": "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.7.0.tgz",
+- "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==",
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.1.0.tgz",
++ "integrity": "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==",
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+- "@clack/core": "1.4.3",
+- "fast-string-width": "^3.0.2",
+- "fast-wrap-ansi": "^0.2.0",
++ "@clack/core": "1.1.0",
+ "sisteransi": "^1.0.5"
+- },
+- "engines": {
+- "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@cloudflare/kv-asset-handler": {
+@@ -974,17 +2107,6 @@
+ "integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==",
+ "license": "MIT"
+ },
+- "node_modules/@emnapi/wasi-threads": {
+- "version": "1.2.3",
+- "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz",
+- "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==",
+- "dev": true,
+- "license": "MIT",
+- "optional": true,
+- "dependencies": {
+- "tslib": "^2.4.0"
+- }
+- },
+ "node_modules/@es-joy/jsdoccomment": {
+ "version": "0.91.0",
+ "resolved": "https://registry.npmmirror.com/@es-joy/jsdoccomment/-/jsdoccomment-0.91.0.tgz",
+@@ -1853,7 +2975,7 @@
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+@@ -1872,7 +2994,7 @@
+ "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==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+@@ -1885,7 +3007,7 @@
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+@@ -1929,7 +3051,7 @@
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+@@ -1944,14 +3066,14 @@
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+@@ -1962,7 +3084,7 @@
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+- "devOptional": true,
++ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+@@ -1975,7 +3097,7 @@
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+@@ -2046,7 +3168,7 @@
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+@@ -2080,7 +3202,7 @@
+ "version": "3.3.7",
+ "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.7.tgz",
+ "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+@@ -2104,14 +3226,14 @@
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.18",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+@@ -2122,7 +3244,7 @@
+ "version": "14.0.0",
+ "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+@@ -2135,7 +3257,7 @@
+ "version": "3.1.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+- "devOptional": true,
++ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+@@ -2148,7 +3270,7 @@
+ "version": "9.39.5",
+ "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+@@ -2161,7 +3283,7 @@
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+@@ -2171,7 +3293,7 @@
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+@@ -2191,7 +3313,7 @@
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+@@ -2201,7 +3323,7 @@
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+@@ -2215,7 +3337,7 @@
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+@@ -2229,7 +3351,7 @@
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+@@ -2700,12 +3822,39 @@
+ }
+ }
+ },
++ "node_modules/@nuxt/cli/node_modules/@clack/core": {
++ "version": "1.4.3",
++ "resolved": "https://registry.npmmirror.com/@clack/core/-/core-1.4.3.tgz",
++ "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==",
++ "license": "MIT",
++ "dependencies": {
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
++ }
++ },
++ "node_modules/@nuxt/cli/node_modules/@clack/prompts": {
++ "version": "1.7.0",
++ "resolved": "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.7.0.tgz",
++ "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==",
++ "license": "MIT",
++ "dependencies": {
++ "@clack/core": "1.4.3",
++ "fast-string-width": "^3.0.2",
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
++ }
++ },
+ "node_modules/@nuxt/cli/node_modules/citty": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmmirror.com/citty/-/citty-0.2.2.tgz",
+ "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==",
+- "license": "MIT",
+- "peer": true
++ "license": "MIT"
+ },
+ "node_modules/@nuxt/devalue": {
+ "version": "2.0.2",
+@@ -2798,6 +3947,34 @@
+ "devtools-wizard": "cli.mjs"
+ }
+ },
++ "node_modules/@nuxt/devtools-wizard/node_modules/@clack/core": {
++ "version": "1.4.3",
++ "resolved": "https://registry.npmmirror.com/@clack/core/-/core-1.4.3.tgz",
++ "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==",
++ "license": "MIT",
++ "dependencies": {
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
++ }
++ },
++ "node_modules/@nuxt/devtools-wizard/node_modules/@clack/prompts": {
++ "version": "1.7.0",
++ "resolved": "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.7.0.tgz",
++ "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==",
++ "license": "MIT",
++ "dependencies": {
++ "@clack/core": "1.4.3",
++ "fast-string-width": "^3.0.2",
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
++ }
++ },
+ "node_modules/@nuxt/devtools/node_modules/isexe": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-4.0.0.tgz",
+@@ -2890,10 +4067,40 @@
+ "eslint": "^9.0.0 || ^10.0.0",
+ "eslint-plugin-format": "*"
+ },
+- "peerDependenciesMeta": {
+- "eslint-plugin-format": {
+- "optional": true
+- }
++ "peerDependenciesMeta": {
++ "eslint-plugin-format": {
++ "optional": true
++ }
++ }
++ },
++ "node_modules/@nuxt/eslint-config/node_modules/@clack/core": {
++ "version": "1.4.3",
++ "resolved": "https://registry.npmmirror.com/@clack/core/-/core-1.4.3.tgz",
++ "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==",
++ "dev": true,
++ "license": "MIT",
++ "dependencies": {
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
++ }
++ },
++ "node_modules/@nuxt/eslint-config/node_modules/@clack/prompts": {
++ "version": "1.7.0",
++ "resolved": "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.7.0.tgz",
++ "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==",
++ "dev": true,
++ "license": "MIT",
++ "dependencies": {
++ "@clack/core": "1.4.3",
++ "fast-string-width": "^3.0.2",
++ "fast-wrap-ansi": "^0.2.0",
++ "sisteransi": "^1.0.5"
++ },
++ "engines": {
++ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@nuxt/eslint-config/node_modules/@eslint/js": {
+@@ -3017,7 +4224,6 @@
+ "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-4.5.2.tgz",
+ "integrity": "sha512-l66LU9DcJYjmNwqwAj2I5UGRrUbnG2DOKGChnN70zIGtn0eq/z87gi/FRgha6eMb9/FmB1PFHgtx6PWVml1C2Q==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "c12": "^3.3.4",
+ "consola": "^3.4.2",
+@@ -3179,7 +4385,6 @@
+ "resolved": "https://registry.npmmirror.com/@nuxt/schema/-/schema-4.5.2.tgz",
+ "integrity": "sha512-h9g1kt9O2iU9nX6HDFu9gvwtpn+8oSJX0RSTWRBnEx/Pkz+WlOHtjuS0SbkTAXw5aT1+H1GysWVwNTjJfBY/0w==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@vue/shared": "^3.5.40",
+ "defu": "^6.1.7",
+@@ -3358,6 +4563,119 @@
+ "node": ">= 4"
+ }
+ },
++ "node_modules/@opentelemetry/api": {
++ "version": "1.9.1",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.1.tgz",
++ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
++ "license": "Apache-2.0",
++ "engines": {
++ "node": ">=8.0.0"
++ }
++ },
++ "node_modules/@opentelemetry/api-logs": {
++ "version": "0.220.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz",
++ "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/api": "^1.3.0"
++ },
++ "engines": {
++ "node": ">=8.0.0"
++ }
++ },
++ "node_modules/@opentelemetry/core": {
++ "version": "2.11.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/core/-/core-2.11.0.tgz",
++ "integrity": "sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/semantic-conventions": "^1.29.0"
++ },
++ "engines": {
++ "node": "^18.19.0 || >=20.6.0"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": ">=1.0.0 <1.10.0"
++ }
++ },
++ "node_modules/@opentelemetry/instrumentation": {
++ "version": "0.220.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz",
++ "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/api-logs": "0.220.0",
++ "import-in-the-middle": "^3.0.0",
++ "require-in-the-middle": "^8.0.0"
++ },
++ "engines": {
++ "node": "^18.19.0 || >=20.6.0"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": "^1.3.0"
++ }
++ },
++ "node_modules/@opentelemetry/resources": {
++ "version": "2.11.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/resources/-/resources-2.11.0.tgz",
++ "integrity": "sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/core": "2.11.0",
++ "@opentelemetry/semantic-conventions": "^1.29.0"
++ },
++ "engines": {
++ "node": "^18.19.0 || >=20.6.0"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": ">=1.3.0 <1.10.0"
++ }
++ },
++ "node_modules/@opentelemetry/sdk-trace": {
++ "version": "2.11.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/sdk-trace/-/sdk-trace-2.11.0.tgz",
++ "integrity": "sha512-fFnTqGm8/G73GQVnxYi7LXa1ZVYEUvgL6XI1LpvV0bPC7WQ/ZGgKxCSl8FnlZBKto9JHHEFTO6s6CUpvvtwFrA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/core": "2.11.0",
++ "@opentelemetry/resources": "2.11.0",
++ "@opentelemetry/semantic-conventions": "^1.29.0"
++ },
++ "engines": {
++ "node": "^18.19.0 || >=20.6.0"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": ">=1.3.0 <1.10.0"
++ }
++ },
++ "node_modules/@opentelemetry/sdk-trace-base": {
++ "version": "2.11.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.11.0.tgz",
++ "integrity": "sha512-H19x/TX/LZdqiYOjM7fqtSxwlplC5pgelavqbQdHbhdq0q/AI/TGkM2dfGuuynTXmJPeF2HoZVoPDu+TGoW78A==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "@opentelemetry/core": "2.11.0",
++ "@opentelemetry/resources": "2.11.0",
++ "@opentelemetry/sdk-trace": "2.11.0",
++ "@opentelemetry/semantic-conventions": "^1.29.0"
++ },
++ "engines": {
++ "node": "^18.19.0 || >=20.6.0"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": ">=1.3.0 <1.10.0"
++ }
++ },
++ "node_modules/@opentelemetry/semantic-conventions": {
++ "version": "1.43.0",
++ "resolved": "https://registry.npmmirror.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz",
++ "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==",
++ "license": "Apache-2.0",
++ "engines": {
++ "node": ">=14"
++ }
++ },
+ "node_modules/@otplib/core": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
+@@ -3413,7 +4731,6 @@
+ "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.148.0.tgz",
+ "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==",
+ "license": "MIT",
+- "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/oxc-project"
+ }
+@@ -3427,6 +4744,41 @@
+ "@noble/hashes": "^1.1.5"
+ }
+ },
++ "node_modules/@parcel/watcher": {
++ "version": "2.6.0",
++ "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.6.0.tgz",
++ "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==",
++ "hasInstallScript": true,
++ "license": "MIT",
++ "optional": true,
++ "dependencies": {
++ "detect-libc": "^2.0.3",
++ "is-glob": "^4.0.3",
++ "node-addon-api": "^7.0.0",
++ "picomatch": "^4.0.4"
++ },
++ "engines": {
++ "node": ">= 10.0.0"
++ },
++ "funding": {
++ "type": "opencollective",
++ "url": "https://opencollective.com/parcel"
++ },
++ "optionalDependencies": {
++ "@parcel/watcher-android-arm64": "2.6.0",
++ "@parcel/watcher-darwin-arm64": "2.6.0",
++ "@parcel/watcher-darwin-x64": "2.6.0",
++ "@parcel/watcher-freebsd-x64": "2.6.0",
++ "@parcel/watcher-linux-arm-glibc": "2.6.0",
++ "@parcel/watcher-linux-arm-musl": "2.6.0",
++ "@parcel/watcher-linux-arm64-glibc": "2.6.0",
++ "@parcel/watcher-linux-arm64-musl": "2.6.0",
++ "@parcel/watcher-linux-x64-glibc": "2.6.0",
++ "@parcel/watcher-linux-x64-musl": "2.6.0",
++ "@parcel/watcher-win32-arm64": "2.6.0",
++ "@parcel/watcher-win32-x64": "2.6.0"
++ }
++ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz",
+@@ -3857,7 +5209,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/app/-/app-7.4.3.tgz",
+ "integrity": "sha512-opyWMuO0Ir8pf1DYUR++wAA6ZfNU+nIX2z95R2OD172HbcdhB4/HD7leLIIAny/LciEdMqlWEBhXK7N93YWbdg==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "7.4.3",
+ "@pixi/display": "7.4.3"
+@@ -3889,7 +5240,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/core/-/core-7.4.3.tgz",
+ "integrity": "sha512-5YDs11faWgVVTL8VZtLU05/Fl47vaP5Tnsbf+y/WRR0VSW3KhRRGTBU1J3Gdc2xEWbJhUK07KGP7eSZpvtPVgA==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@pixi/color": "7.4.3",
+ "@pixi/constants": "7.4.3",
+@@ -3910,7 +5260,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/display/-/display-7.4.3.tgz",
+ "integrity": "sha512-b5m2dAaoNAVdxz1oDaxl3XZ059NEOcNtGkxTOZ4EYCw/jcp9sZXkgSROHRzsGn4k+NugH7+9MP4Id2Z0kkdUhw==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "7.4.3"
+ }
+@@ -3926,7 +5275,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/filter-blur/-/filter-blur-7.4.3.tgz",
+ "integrity": "sha512-ZFzS9L/whdRbs5A/EUgF3yQaBcxNarmbuwaMgrfnpQ84mRczkGByqDLGToadiufyals07ufTrXBGRle9lbtEDA==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "7.4.3"
+ }
+@@ -3936,7 +5284,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/filter-bulge-pinch/-/filter-bulge-pinch-5.1.1.tgz",
+ "integrity": "sha512-80I3g813td7Fnzi7IJSiR3z8gZlKblk6WN+5z6WnscQROcNEpck6lgWS/Lf/IdeHB/FtUKJCbx7RzxkUhiRTvA==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "^7.0.0-X"
+ }
+@@ -3946,7 +5293,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/filter-color-matrix/-/filter-color-matrix-7.4.3.tgz",
+ "integrity": "sha512-TNu0h20SrzjUWIb5v19dAp1vPpqtG0w2XF9kIHN91bMNaf3R1jzhpWG6TtaVO9eo1IolWcEJLw38jIohyC+KNw==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "7.4.3"
+ }
+@@ -3979,7 +5325,6 @@
+ "resolved": "https://registry.npmjs.org/@pixi/sprite/-/sprite-7.4.3.tgz",
+ "integrity": "sha512-iNBrpOFF9nXDT6m2jcyYy6l/sRzklLDDck1eFHprHZwvNquY2nzRfh+RGBCecxhBcijiLJ3fsZN33fP0LDXkvw==",
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "@pixi/core": "7.4.3",
+ "@pixi/display": "7.4.3"
+@@ -4079,7 +5424,6 @@
+ "resolved": "https://registry.npmmirror.com/@redis/client/-/client-5.12.1.tgz",
+ "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "cluster-key-slot": "1.1.2"
+ },
+@@ -4407,6 +5751,32 @@
+ }
+ }
+ },
++ "node_modules/@rollup/plugin-babel": {
++ "version": "6.1.0",
++ "resolved": "https://registry.npmmirror.com/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz",
++ "integrity": "sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-module-imports": "^7.18.6",
++ "@rollup/pluginutils": "^5.0.1"
++ },
++ "engines": {
++ "node": ">=14.0.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.0.0",
++ "@types/babel__core": "^7.1.9",
++ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
++ },
++ "peerDependenciesMeta": {
++ "@types/babel__core": {
++ "optional": true
++ },
++ "rollup": {
++ "optional": true
++ }
++ }
++ },
+ "node_modules/@rollup/plugin-commonjs": {
+ "version": "29.0.3",
+ "resolved": "https://registry.npmmirror.com/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz",
+@@ -4906,58 +6276,210 @@
+ "win32"
+ ]
+ },
+- "node_modules/@sansenjian/qq-music-api": {
+- "version": "2.6.0",
+- "resolved": "https://registry.npmmirror.com/@sansenjian/qq-music-api/-/qq-music-api-2.6.0.tgz",
+- "integrity": "sha512-eOKIcXLL4TL4Iv0lt7O5bZbp+t6bYzoZtp4fXumjGBj6sT58BnEqoi5tSBHWELMibakUljbpBdkZzAXfFQN9iQ==",
++ "node_modules/@sentry/browser": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/browser/-/browser-10.73.0.tgz",
++ "integrity": "sha512-HqTe1S5RrWLufhX2LaFP3yNoMxfNDroh120bq1zdGHZfFDBMJQ0CDXxHO+L4UJfQ5dWdCCzWbXIAiZuWGa/DFQ==",
+ "license": "MIT",
+- "workspaces": [
+- "packages/mcp"
+- ],
+ "dependencies": {
+- "@koa/router": "^15.5.0",
+- "axios": "^1.16.1",
+- "koa": "^2.16.4"
++ "@sentry/browser-utils": "10.73.0",
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0",
++ "@sentry/feedback": "10.73.0",
++ "@sentry/replay": "10.73.0",
++ "@sentry/replay-canvas": "10.73.0"
+ },
+- "bin": {
+- "qq-music-api": "dist/cli.js"
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/browser-utils": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/browser-utils/-/browser-utils-10.73.0.tgz",
++ "integrity": "sha512-qQygxJZ+RV779+iL1+lrJ4f4sZLgbgW0/JWPNp0YlcEAE62yCsdKbqoTEjB/EugdS4mSjBMX0chZC6rblu2Ycw==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0"
+ },
+ "engines": {
+- "node": "^20.17.0 || >=22.9.0",
+- "npm": "^11.0.0"
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/conventions": {
++ "version": "0.16.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/conventions/-/conventions-0.16.0.tgz",
++ "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=14"
+ }
+ },
+- "node_modules/@sansenjian/qq-music-api/node_modules/@koa/router": {
+- "version": "15.7.0",
+- "resolved": "https://registry.npmmirror.com/@koa/router/-/router-15.7.0.tgz",
+- "integrity": "sha512-WaAlk4TOl/O0rhTpOR0l052gz03syPMmI6Pe2gd7v3ubjfv5UcSGcnb0Y/J5NNC/ln+5FiUqPJTc/a15I+XqAA==",
++ "node_modules/@sentry/core": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/core/-/core-10.73.0.tgz",
++ "integrity": "sha512-FLO1UgH19RyasVpofu612WCOgb2nEH0dZy+R72d7p65XU9i0wxlMKm3+sgfwKmiSJp1Qhilaaxs4Jg6BbiM5HA==",
+ "license": "MIT",
+ "dependencies": {
+- "debug": "^4.4.3",
+- "http-errors": "^2.0.1",
+- "koa-compose": "^4.1.0",
+- "path-to-regexp": "^8.4.2"
++ "@sentry/conventions": "^0.16.0"
+ },
+ "engines": {
+- "node": ">= 20"
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/feedback": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/feedback/-/feedback-10.73.0.tgz",
++ "integrity": "sha512-D6nSngX+e46Mae2/oh2bxBvxNK1z2NERbuMAhB5sx9x4xMBWIyGnYYTECehvEqV9+AqGAgxxhOZoYIG3AmRwww==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/core": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/node": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/node/-/node-10.73.0.tgz",
++ "integrity": "sha512-jiMJ6GgXDw6UMGzJY+o0c8OoeA9OfHqZ/xEpHfDqy75hn+9CEkRkbNGCIdMvsc7wW/Se1Os394hHfTpU+YEskg==",
++ "license": "MIT",
++ "dependencies": {
++ "@opentelemetry/api": "^1.9.1",
++ "@opentelemetry/instrumentation": "^0.220.0",
++ "@opentelemetry/sdk-trace-base": "^2.9.0",
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0",
++ "@sentry/node-core": "10.73.0",
++ "@sentry/opentelemetry": "10.73.0",
++ "@sentry/server-utils": "10.73.0",
++ "import-in-the-middle": "^3.0.0"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/node-core": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/node-core/-/node-core-10.73.0.tgz",
++ "integrity": "sha512-GHAGUmZPmm6FKfxfv2maVVJ/A99YAmd7oFOuKMDmITI6O/Msl6JB3vPTEpopVAHLW0LYJQBOjddL9C7o+Jt44g==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0",
++ "@sentry/opentelemetry": "10.73.0",
++ "import-in-the-middle": "^3.0.0"
++ },
++ "engines": {
++ "node": ">=18"
+ },
+ "peerDependencies": {
+- "koa": "^2.0.0 || ^3.0.0"
++ "@opentelemetry/api": "^1.9.0",
++ "@opentelemetry/core": "^1.30.1 || ^2.1.0",
++ "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1",
++ "@opentelemetry/instrumentation": ">=0.57.1 <1",
++ "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
+ },
+ "peerDependenciesMeta": {
+- "koa": {
+- "optional": false
++ "@opentelemetry/api": {
++ "optional": true
++ },
++ "@opentelemetry/core": {
++ "optional": true
++ },
++ "@opentelemetry/exporter-trace-otlp-http": {
++ "optional": true
++ },
++ "@opentelemetry/instrumentation": {
++ "optional": true
++ },
++ "@opentelemetry/sdk-trace-base": {
++ "optional": true
+ }
+ }
+ },
+- "node_modules/@sansenjian/qq-music-api/node_modules/path-to-regexp": {
+- "version": "8.4.2",
+- "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+- "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
++ "node_modules/@sentry/opentelemetry": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/opentelemetry/-/opentelemetry-10.73.0.tgz",
++ "integrity": "sha512-fQouPQKsH0CQrw6oAn1k0Z2I+tgyochCovifr5qNS69i0OzjknLa03WJyiZ/IuzXc4AVa5jAKfOeE9slABz8Qw==",
+ "license": "MIT",
+- "funding": {
+- "type": "opencollective",
+- "url": "https://opencollective.com/express"
++ "dependencies": {
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ },
++ "peerDependencies": {
++ "@opentelemetry/api": "^1.9.0",
++ "@opentelemetry/core": "^1.30.1 || ^2.1.0",
++ "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0"
++ }
++ },
++ "node_modules/@sentry/replay": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/replay/-/replay-10.73.0.tgz",
++ "integrity": "sha512-nN2wjN/Y0J5BOJV5hqRHUEBfxwUsipp1PKjcDHh6Fpxnrtfldu3Y99E8cQInseo5heFdzEvrOHBBrqWZXOHVKQ==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/browser-utils": "10.73.0",
++ "@sentry/core": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/replay-canvas": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/replay-canvas/-/replay-canvas-10.73.0.tgz",
++ "integrity": "sha512-sxa2lKkHPfF/j5xFpW7gocthWXRqyoHz8KCPy6yGc8plT477nl57iGSKhQDYsx1Ny10TLjs7YkIuPP1GY/Ax2Q==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/core": "10.73.0",
++ "@sentry/replay": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/server-utils": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/server-utils/-/server-utils-10.73.0.tgz",
++ "integrity": "sha512-QskripdKFbM/+gipC6mpa2crLwL7+VbkX84IpHg2z9UlYQ1kNKd3aMT+Qk9NLRSw/zu1rSIAvlbWfx4D3rgNAA==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/@sentry/vue": {
++ "version": "10.73.0",
++ "resolved": "https://registry.npmmirror.com/@sentry/vue/-/vue-10.73.0.tgz",
++ "integrity": "sha512-pbUt65YO7mcLT8q0geK62K4HxlKQkya3ZNqXo+BRz99uMvC78BdNSK+ME4jvTYeHwRJdLqQfAwrcXvKgAQlweg==",
++ "license": "MIT",
++ "dependencies": {
++ "@sentry/browser": "10.73.0",
++ "@sentry/conventions": "^0.16.0",
++ "@sentry/core": "10.73.0"
++ },
++ "engines": {
++ "node": ">=18"
++ },
++ "peerDependencies": {
++ "@tanstack/vue-router": "^1.64.0",
++ "pinia": "2.x || 3.x || 4.x",
++ "vue": "2.x || 3.x"
++ },
++ "peerDependenciesMeta": {
++ "@tanstack/vue-router": {
++ "optional": true
++ },
++ "pinia": {
++ "optional": true
++ }
+ }
+ },
+ "node_modules/@simple-git/args-pathspec": {
+@@ -5056,7 +6578,6 @@
+ "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==",
+ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/types": "^8.56.0",
+@@ -5281,7 +6802,6 @@
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz",
+ "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==",
+ "license": "Apache-2.0",
+- "peer": true,
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+@@ -5301,6 +6821,21 @@
+ "integrity": "sha512-zeSORFArxqUwfVMTRHu8AN9k9LlfSn0CKDSzLhJDITpgLoS0xpnocxsgMjQjUcVYDgO47r9zLP49HEjH/iGsFg==",
+ "license": "Apache-2.0"
+ },
++ "node_modules/@trickfilm400/rollup-plugin-off-main-thread": {
++ "version": "3.0.0-pre1",
++ "resolved": "https://registry.npmmirror.com/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz",
++ "integrity": "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "ejs": "^3.1.10",
++ "json5": "^2.2.3",
++ "magic-string": "^0.30.21",
++ "string.prototype.matchall": "^4.0.12"
++ },
++ "engines": {
++ "node": ">=12"
++ }
++ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+@@ -5388,9 +6923,8 @@
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+- "devOptional": true,
+- "license": "MIT",
+- "peer": true
++ "dev": true,
++ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.10",
+@@ -5461,7 +6995,6 @@
+ "resolved": "https://registry.npmmirror.com/@types/pg/-/pg-8.20.0.tgz",
+ "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@types/node": "*",
+ "pg-protocol": "*",
+@@ -5516,8 +7049,7 @@
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+- "license": "MIT",
+- "optional": true
++ "license": "MIT"
+ },
+ "node_modules/@types/xml2js": {
+ "version": "0.4.14",
+@@ -5573,7 +7105,6 @@
+ "integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==",
+ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.69.0",
+ "@typescript-eslint/types": "8.69.0",
+@@ -6282,6 +7813,82 @@
+ }
+ }
+ },
++ "node_modules/@vite-pwa/nuxt": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/@vite-pwa/nuxt/-/nuxt-1.1.1.tgz",
++ "integrity": "sha512-8RkdxNJ3NubtwmS96bPVvDclFmysH3VADKWqlaBaUu6PLsoHM3Wq7ThdYBsAT6EpqzWS+iFC2SF6lkJ4xDhKLA==",
++ "license": "MIT",
++ "dependencies": {
++ "@nuxt/kit": "^3.9.0",
++ "pathe": "^1.1.1",
++ "ufo": "^1.3.2",
++ "vite-plugin-pwa": "^1.2.0"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/antfu"
++ },
++ "peerDependencies": {
++ "@vite-pwa/assets-generator": "^1.0.0"
++ },
++ "peerDependenciesMeta": {
++ "@vite-pwa/assets-generator": {
++ "optional": true
++ }
++ }
++ },
++ "node_modules/@vite-pwa/nuxt/node_modules/@nuxt/kit": {
++ "version": "3.21.11",
++ "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.21.11.tgz",
++ "integrity": "sha512-0Xi3tgwN77w43Q8GCPIrvWmF1J7Peehkts44E0uKNIml9lB8WoUn8YxyUjxBv47XtVR86NWoWALAT+/IEMHJEA==",
++ "license": "MIT",
++ "dependencies": {
++ "c12": "^3.3.4",
++ "consola": "^3.4.2",
++ "defu": "^6.1.7",
++ "destr": "^2.0.5",
++ "errx": "^0.1.2",
++ "exsolve": "^1.1.1",
++ "ignore": "^7.0.6",
++ "jiti": "^2.7.0",
++ "klona": "^2.0.6",
++ "knitwork": "^1.3.0",
++ "mlly": "^1.8.2",
++ "ohash": "^2.0.11",
++ "pathe": "^2.0.3",
++ "pkg-types": "^2.3.1",
++ "rc9": "^3.0.1",
++ "scule": "^1.3.0",
++ "semver": "^7.8.5",
++ "tinyglobby": "^0.2.17",
++ "ufo": "^1.6.4",
++ "unctx": "^2.5.0",
++ "untyped": "^2.0.0"
++ },
++ "engines": {
++ "node": ">=18.12.0"
++ }
++ },
++ "node_modules/@vite-pwa/nuxt/node_modules/@nuxt/kit/node_modules/pathe": {
++ "version": "2.0.3",
++ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
++ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
++ "license": "MIT"
++ },
++ "node_modules/@vite-pwa/nuxt/node_modules/ignore": {
++ "version": "7.0.8",
++ "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.8.tgz",
++ "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 4"
++ }
++ },
++ "node_modules/@vite-pwa/nuxt/node_modules/pathe": {
++ "version": "1.1.2",
++ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
++ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
++ "license": "MIT"
++ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "6.0.8",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz",
+@@ -6423,7 +8030,6 @@
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
+ "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@vue/compiler-core": "3.5.42",
+@@ -6585,7 +8191,6 @@
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz",
+ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
+ "license": "MIT",
+- "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+@@ -6606,7 +8211,7 @@
+ "version": "5.3.2",
+ "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+@@ -6634,7 +8239,7 @@
+ "version": "6.15.0",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+@@ -6907,9 +8512,46 @@
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Python-2.0"
+ },
++ "node_modules/array-buffer-byte-length": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
++ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "is-array-buffer": "^3.0.5"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/arraybuffer.prototype.slice": {
++ "version": "1.0.4",
++ "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
++ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
++ "license": "MIT",
++ "dependencies": {
++ "array-buffer-byte-length": "^1.0.1",
++ "call-bind": "^1.0.8",
++ "define-properties": "^1.2.1",
++ "es-abstract": "^1.23.5",
++ "es-errors": "^1.3.0",
++ "get-intrinsic": "^1.2.6",
++ "is-array-buffer": "^3.0.4"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+@@ -6969,6 +8611,15 @@
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "license": "MIT"
+ },
++ "node_modules/async-function": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz",
++ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
+ "node_modules/async-sema": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/async-sema/-/async-sema-3.1.1.tgz",
+@@ -7026,6 +8677,21 @@
+ "postcss": "^8.1.0"
+ }
+ },
++ "node_modules/available-typed-arrays": {
++ "version": "1.0.7",
++ "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
++ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
++ "license": "MIT",
++ "dependencies": {
++ "possible-typed-array-names": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/axios": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.20.0.tgz",
+@@ -7077,6 +8743,54 @@
+ }
+ }
+ },
++ "node_modules/babel-plugin-polyfill-corejs2": {
++ "version": "0.4.17",
++ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
++ "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/compat-data": "^7.28.6",
++ "@babel/helper-define-polyfill-provider": "^0.6.8",
++ "semver": "^6.3.1"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
++ }
++ },
++ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
++ "version": "6.3.1",
++ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
++ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
++ "license": "ISC",
++ "bin": {
++ "semver": "bin/semver.js"
++ }
++ },
++ "node_modules/babel-plugin-polyfill-corejs3": {
++ "version": "0.14.2",
++ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
++ "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-define-polyfill-provider": "^0.6.8",
++ "core-js-compat": "^3.48.0"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
++ }
++ },
++ "node_modules/babel-plugin-polyfill-regenerator": {
++ "version": "0.6.8",
++ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
++ "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
++ "license": "MIT",
++ "dependencies": {
++ "@babel/helper-define-polyfill-provider": "^0.6.8"
++ },
++ "peerDependencies": {
++ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
++ }
++ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz",
+@@ -7091,7 +8805,6 @@
+ "resolved": "https://registry.npmmirror.com/bare-events/-/bare-events-2.9.2.tgz",
+ "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==",
+ "license": "Apache-2.0",
+- "peer": true,
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+@@ -7300,7 +9013,6 @@
+ }
+ ],
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.11.20",
+ "caniuse-lite": "^1.0.30001810",
+@@ -7477,6 +9189,24 @@
+ "node": ">= 6.0.0"
+ }
+ },
++ "node_modules/call-bind": {
++ "version": "1.0.9",
++ "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz",
++ "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind-apply-helpers": "^1.0.2",
++ "es-define-property": "^1.0.1",
++ "get-intrinsic": "^1.3.0",
++ "set-function-length": "^1.2.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+@@ -7510,7 +9240,7 @@
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+@@ -7686,6 +9416,12 @@
+ "consola": "^3.2.3"
+ }
+ },
++ "node_modules/cjs-module-lexer": {
++ "version": "2.2.1",
++ "resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz",
++ "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==",
++ "license": "MIT"
++ },
+ "node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+@@ -7774,6 +9510,15 @@
+ "node": ">= 12.0.0"
+ }
+ },
++ "node_modules/common-tags": {
++ "version": "1.8.2",
++ "resolved": "https://registry.npmmirror.com/common-tags/-/common-tags-1.8.2.tgz",
++ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=4.0.0"
++ }
++ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz",
+@@ -7963,7 +9708,6 @@
+ "version": "3.50.0",
+ "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.50.0.tgz",
+ "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==",
+- "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.7"
+@@ -8080,6 +9824,15 @@
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+ "license": "MIT"
+ },
++ "node_modules/crypto-random-string": {
++ "version": "2.0.0",
++ "resolved": "https://registry.npmmirror.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
++ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=8"
++ }
++ },
+ "node_modules/css-line-break": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+@@ -8263,6 +10016,57 @@
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
++ "node_modules/data-view-buffer": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
++ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "es-errors": "^1.3.0",
++ "is-data-view": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/data-view-byte-length": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
++ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "es-errors": "^1.3.0",
++ "is-data-view": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/inspect-js"
++ }
++ },
++ "node_modules/data-view-byte-offset": {
++ "version": "1.0.1",
++ "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
++ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.2",
++ "es-errors": "^1.3.0",
++ "is-data-view": "^1.0.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/dayjs": {
+ "version": "1.11.23",
+ "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.23.tgz",
+@@ -8345,7 +10149,7 @@
+ "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==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+@@ -8385,6 +10189,23 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/define-data-property": {
++ "version": "1.1.4",
++ "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
++ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
++ "license": "MIT",
++ "dependencies": {
++ "es-define-property": "^1.0.0",
++ "es-errors": "^1.3.0",
++ "gopd": "^1.0.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+@@ -8397,6 +10218,23 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/define-properties": {
++ "version": "1.2.1",
++ "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
++ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
++ "license": "MIT",
++ "dependencies": {
++ "define-data-property": "^1.0.1",
++ "has-property-descriptors": "^1.0.0",
++ "object-keys": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.7.tgz",
+@@ -8519,7 +10357,6 @@
+ "integrity": "sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==",
+ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "peerDependencies": {
+ "srvx": ">=0.11.5"
+ },
+@@ -8564,7 +10401,6 @@
+ "integrity": "sha512-aOuk+yNJA61yA6eJJvQJxpJLIxz090hKMqDGzd35tGsvwaodi1RPuYSFUp5EINaJp7snjVwLkO2RXVMPZBYnnA==",
+ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "bin": {
+ "srvx": "bin/srvx.mjs"
+ },
+@@ -9193,7 +11029,6 @@
+ "resolved": "https://registry.npmmirror.com/drizzle-orm/-/drizzle-orm-0.45.2.tgz",
+ "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==",
+ "license": "Apache-2.0",
+- "peer": true,
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+@@ -9361,6 +11196,21 @@
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
++ "node_modules/ejs": {
++ "version": "3.1.10",
++ "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
++ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "jake": "^10.8.5"
++ },
++ "bin": {
++ "ejs": "bin/cli.js"
++ },
++ "engines": {
++ "node": ">=0.10.0"
++ }
++ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.422",
+ "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz",
+@@ -9409,6 +11259,92 @@
+ "integrity": "sha512-chfpPHmCerdo/rXr/nNvPZRkV4WwDRwzwnsJ0Uzz3tVi8Z41tDctRjduYy1138ii77AFlts1qvWtX3g/Acg91Q==",
+ "license": "MIT"
+ },
++ "node_modules/es-abstract": {
++ "version": "1.24.2",
++ "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.2.tgz",
++ "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",
++ "license": "MIT",
++ "dependencies": {
++ "array-buffer-byte-length": "^1.0.2",
++ "arraybuffer.prototype.slice": "^1.0.4",
++ "available-typed-arrays": "^1.0.7",
++ "call-bind": "^1.0.8",
++ "call-bound": "^1.0.4",
++ "data-view-buffer": "^1.0.2",
++ "data-view-byte-length": "^1.0.2",
++ "data-view-byte-offset": "^1.0.1",
++ "es-define-property": "^1.0.1",
++ "es-errors": "^1.3.0",
++ "es-object-atoms": "^1.1.1",
++ "es-set-tostringtag": "^2.1.0",
++ "es-to-primitive": "^1.3.0",
++ "function.prototype.name": "^1.1.8",
++ "get-intrinsic": "^1.3.0",
++ "get-proto": "^1.0.1",
++ "get-symbol-description": "^1.1.0",
++ "globalthis": "^1.0.4",
++ "gopd": "^1.2.0",
++ "has-property-descriptors": "^1.0.2",
++ "has-proto": "^1.2.0",
++ "has-symbols": "^1.1.0",
++ "hasown": "^2.0.2",
++ "internal-slot": "^1.1.0",
++ "is-array-buffer": "^3.0.5",
++ "is-callable": "^1.2.7",
++ "is-data-view": "^1.0.2",
++ "is-negative-zero": "^2.0.3",
++ "is-regex": "^1.2.1",
++ "is-set": "^2.0.3",
++ "is-shared-array-buffer": "^1.0.4",
++ "is-string": "^1.1.1",
++ "is-typed-array": "^1.1.15",
++ "is-weakref": "^1.1.1",
++ "math-intrinsics": "^1.1.0",
++ "object-inspect": "^1.13.4",
++ "object-keys": "^1.1.1",
++ "object.assign": "^4.1.7",
++ "own-keys": "^1.0.1",
++ "regexp.prototype.flags": "^1.5.4",
++ "safe-array-concat": "^1.1.3",
++ "safe-push-apply": "^1.0.0",
++ "safe-regex-test": "^1.1.0",
++ "set-proto": "^1.0.0",
++ "stop-iteration-iterator": "^1.1.0",
++ "string.prototype.trim": "^1.2.10",
++ "string.prototype.trimend": "^1.0.9",
++ "string.prototype.trimstart": "^1.0.8",
++ "typed-array-buffer": "^1.0.3",
++ "typed-array-byte-length": "^1.0.3",
++ "typed-array-byte-offset": "^1.0.4",
++ "typed-array-length": "^1.0.7",
++ "unbox-primitive": "^1.1.0",
++ "which-typed-array": "^1.1.19"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/es-abstract-get": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/es-abstract-get/-/es-abstract-get-1.0.0.tgz",
++ "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",
++ "license": "MIT",
++ "dependencies": {
++ "es-errors": "^1.3.0",
++ "es-object-atoms": "^1.1.2",
++ "is-callable": "^1.2.7",
++ "object-inspect": "^1.13.4"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+@@ -9434,9 +11370,9 @@
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+- "version": "1.1.1",
+- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
++ "version": "1.1.2",
++ "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
++ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+@@ -9460,13 +11396,32 @@
+ "node": ">= 0.4"
+ }
+ },
++ "node_modules/es-to-primitive": {
++ "version": "1.3.4",
++ "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.4.tgz",
++ "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==",
++ "license": "MIT",
++ "dependencies": {
++ "es-abstract-get": "^1.0.0",
++ "es-define-property": "^1.0.1",
++ "es-errors": "^1.3.0",
++ "is-callable": "^1.2.7",
++ "is-date-object": "^1.1.0",
++ "is-symbol": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "hasInstallScript": true,
+ "license": "MIT",
+- "peer": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+@@ -9521,7 +11476,7 @@
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+@@ -9535,9 +11490,8 @@
+ "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.5.tgz",
+ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+@@ -9892,7 +11846,7 @@
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+@@ -9926,7 +11880,7 @@
+ "version": "4.2.1",
+ "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+@@ -9939,14 +11893,14 @@
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+@@ -9957,7 +11911,7 @@
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+@@ -9974,7 +11928,7 @@
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+@@ -9991,7 +11945,7 @@
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+@@ -10007,7 +11961,7 @@
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+- "devOptional": true,
++ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+@@ -10020,7 +11974,7 @@
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+@@ -10036,7 +11990,7 @@
+ "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==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+@@ -10052,7 +12006,7 @@
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+@@ -10065,7 +12019,7 @@
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+@@ -10078,7 +12032,7 @@
+ "version": "10.4.0",
+ "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+@@ -10096,7 +12050,7 @@
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+@@ -10109,7 +12063,7 @@
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+@@ -10122,7 +12076,7 @@
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+@@ -10138,12 +12092,23 @@
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+- "devOptional": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
++ "node_modules/eta": {
++ "version": "4.6.0",
++ "resolved": "https://registry.npmmirror.com/eta/-/eta-4.6.0.tgz",
++ "integrity": "sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=20"
++ },
++ "funding": {
++ "url": "https://github.com/bgub/eta?sponsor=1"
++ }
++ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
+@@ -10219,7 +12184,6 @@
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+- "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+@@ -10260,14 +12224,13 @@
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+- "devOptional": true,
+ "license": "MIT"
+ },
+ "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==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-npm-meta": {
+@@ -10311,6 +12274,22 @@
+ "fast-string-truncated-width": "^3.0.2"
+ }
+ },
++ "node_modules/fast-uri": {
++ "version": "3.1.7",
++ "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.7.tgz",
++ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
++ "funding": [
++ {
++ "type": "github",
++ "url": "https://github.com/sponsors/fastify"
++ },
++ {
++ "type": "opencollective",
++ "url": "https://opencollective.com/fastify"
++ }
++ ],
++ "license": "BSD-3-Clause"
++ },
+ "node_modules/fast-wrap-ansi": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmmirror.com/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz",
+@@ -10356,7 +12335,7 @@
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+@@ -10371,6 +12350,42 @@
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
++ "node_modules/filelist": {
++ "version": "1.0.6",
++ "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz",
++ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "minimatch": "^5.0.1"
++ }
++ },
++ "node_modules/filelist/node_modules/balanced-match": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
++ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
++ "license": "MIT"
++ },
++ "node_modules/filelist/node_modules/brace-expansion": {
++ "version": "2.1.4",
++ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
++ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
++ "license": "MIT",
++ "dependencies": {
++ "balanced-match": "^1.0.0"
++ }
++ },
++ "node_modules/filelist/node_modules/minimatch": {
++ "version": "5.1.9",
++ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
++ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
++ "license": "ISC",
++ "dependencies": {
++ "brace-expansion": "^2.0.1"
++ },
++ "engines": {
++ "node": ">=10"
++ }
++ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+@@ -10417,7 +12432,7 @@
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+@@ -10431,7 +12446,7 @@
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
+ "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fnv1a-64": {
+@@ -10460,6 +12475,21 @@
+ }
+ }
+ },
++ "node_modules/for-each": {
++ "version": "0.3.5",
++ "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
++ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
++ "license": "MIT",
++ "dependencies": {
++ "is-callable": "^1.2.7"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+@@ -10597,6 +12627,38 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/function.prototype.name": {
++ "version": "1.2.0",
++ "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
++ "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "es-define-property": "^1.0.1",
++ "es-errors": "^1.3.0",
++ "functions-have-names": "^1.2.3",
++ "has-property-descriptors": "^1.0.2",
++ "hasown": "^2.0.4",
++ "is-callable": "^1.2.7",
++ "is-document.all": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/functions-have-names": {
++ "version": "1.2.3",
++ "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz",
++ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
++ "license": "MIT",
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/fuse.js": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmmirror.com/fuse.js/-/fuse.js-7.5.0.tgz",
+@@ -10688,6 +12750,12 @@
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
++ "node_modules/get-own-enumerable-property-symbols": {
++ "version": "3.0.2",
++ "resolved": "https://registry.npmmirror.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
++ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
++ "license": "ISC"
++ },
+ "node_modules/get-port-please": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz",
+@@ -10719,6 +12787,23 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/get-symbol-description": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
++ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "es-errors": "^1.3.0",
++ "get-intrinsic": "^1.2.6"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+@@ -10803,6 +12888,22 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/globalthis": {
++ "version": "1.0.4",
++ "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz",
++ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
++ "license": "MIT",
++ "dependencies": {
++ "define-properties": "^1.2.1",
++ "gopd": "^1.0.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/globby": {
+ "version": "16.2.4",
+ "resolved": "https://registry.npmmirror.com/globby/-/globby-16.2.4.tgz",
+@@ -10895,6 +12996,18 @@
+ "uncrypto": "^0.1.3"
+ }
+ },
++ "node_modules/has-bigints": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz",
++ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+@@ -10904,6 +13017,33 @@
+ "node": ">=8"
+ }
+ },
++ "node_modules/has-property-descriptors": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
++ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
++ "license": "MIT",
++ "dependencies": {
++ "es-define-property": "^1.0.0"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/has-proto": {
++ "version": "1.2.0",
++ "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz",
++ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
++ "license": "MIT",
++ "dependencies": {
++ "dunder-proto": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+@@ -11116,6 +13256,12 @@
+ "node": ">=16.17.0"
+ }
+ },
++ "node_modules/idb": {
++ "version": "7.1.1",
++ "resolved": "https://registry.npmmirror.com/idb/-/idb-7.1.1.tgz",
++ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
++ "license": "ISC"
++ },
+ "node_modules/identifier-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/identifier-regex/-/identifier-regex-1.1.0.tgz",
+@@ -11183,7 +13329,7 @@
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+@@ -11196,6 +13342,20 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/import-in-the-middle": {
++ "version": "3.4.0",
++ "resolved": "https://registry.npmmirror.com/import-in-the-middle/-/import-in-the-middle-3.4.0.tgz",
++ "integrity": "sha512-Xfjwfarhe+LGmoaof+sexeNo3sGRysb5x56WrZLwHtmqT0OilSKtVWkv0lrCcMgSKyKP9oBogBAisHZvtJH0aw==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "cjs-module-lexer": "^2.2.0",
++ "es-module-lexer": "^2.2.0",
++ "module-details-from-path": "^1.0.4"
++ },
++ "engines": {
++ "node": ">=18"
++ }
++ },
+ "node_modules/impound": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/impound/-/impound-1.2.0.tgz",
+@@ -11213,7 +13373,7 @@
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+@@ -11258,6 +13418,20 @@
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
++ "node_modules/internal-slot": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
++ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
++ "license": "MIT",
++ "dependencies": {
++ "es-errors": "^1.3.0",
++ "hasown": "^2.0.2",
++ "side-channel": "^1.1.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
+ "node_modules/iobuffer": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
+@@ -11295,6 +13469,57 @@
+ "url": "https://github.com/sponsors/brc-dd"
+ }
+ },
++ "node_modules/is-array-buffer": {
++ "version": "3.0.5",
++ "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
++ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.8",
++ "call-bound": "^1.0.3",
++ "get-intrinsic": "^1.2.6"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-async-function": {
++ "version": "2.1.1",
++ "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz",
++ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
++ "license": "MIT",
++ "dependencies": {
++ "async-function": "^1.0.0",
++ "call-bound": "^1.0.3",
++ "get-proto": "^1.0.1",
++ "has-tostringtag": "^1.0.2",
++ "safe-regex-test": "^1.1.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-bigint": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz",
++ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
++ "license": "MIT",
++ "dependencies": {
++ "has-bigints": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+@@ -11307,6 +13532,22 @@
+ "node": ">=8"
+ }
+ },
++ "node_modules/is-boolean-object": {
++ "version": "1.2.2",
++ "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
++ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "has-tostringtag": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-builtin-module": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
+@@ -11323,6 +13564,18 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/is-callable": {
++ "version": "1.2.7",
++ "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
++ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+@@ -11338,6 +13591,39 @@
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
++ "node_modules/is-data-view": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz",
++ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.2",
++ "get-intrinsic": "^1.2.6",
++ "is-typed-array": "^1.1.13"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-date-object": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz",
++ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.2",
++ "has-tostringtag": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz",
+@@ -11353,13 +13639,43 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/is-document.all": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/is-document.all/-/is-document.all-1.0.0.tgz",
++ "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.4"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+- "node": ">=0.10.0"
++ "node": ">=0.10.0"
++ }
++ },
++ "node_modules/is-finalizationregistry": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
++ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+@@ -11465,12 +13781,36 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/is-map": {
++ "version": "2.0.3",
++ "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz",
++ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+ "license": "MIT"
+ },
++ "node_modules/is-negative-zero": {
++ "version": "2.0.3",
++ "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
++ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+@@ -11480,6 +13820,31 @@
+ "node": ">=0.12.0"
+ }
+ },
++ "node_modules/is-number-object": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz",
++ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "has-tostringtag": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-obj": {
++ "version": "1.0.1",
++ "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-1.0.1.tgz",
++ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=0.10.0"
++ }
++ },
+ "node_modules/is-path-inside": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-4.0.0.tgz",
+@@ -11519,6 +13884,42 @@
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
++ "node_modules/is-regexp": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/is-regexp/-/is-regexp-1.0.0.tgz",
++ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=0.10.0"
++ }
++ },
++ "node_modules/is-set": {
++ "version": "2.0.3",
++ "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz",
++ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-shared-array-buffer": {
++ "version": "1.0.4",
++ "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
++ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz",
+@@ -11531,6 +13932,97 @@
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
++ "node_modules/is-string": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz",
++ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "has-tostringtag": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-symbol": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz",
++ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.2",
++ "has-symbols": "^1.1.0",
++ "safe-regex-test": "^1.1.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-typed-array": {
++ "version": "1.1.15",
++ "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz",
++ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
++ "license": "MIT",
++ "dependencies": {
++ "which-typed-array": "^1.1.16"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-weakmap": {
++ "version": "2.0.2",
++ "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz",
++ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-weakref": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz",
++ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/is-weakset": {
++ "version": "2.0.4",
++ "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz",
++ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "get-intrinsic": "^1.2.6"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/is-wsl": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.1.tgz",
+@@ -11579,6 +14071,23 @@
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
++ "node_modules/jake": {
++ "version": "10.9.4",
++ "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz",
++ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
++ "license": "Apache-2.0",
++ "dependencies": {
++ "async": "^3.2.6",
++ "filelist": "^1.0.4",
++ "picocolors": "^1.1.1"
++ },
++ "bin": {
++ "jake": "bin/cli.js"
++ },
++ "engines": {
++ "node": ">=10"
++ }
++ },
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.7.0.tgz",
+@@ -11598,7 +14107,7 @@
+ "version": "4.3.2",
+ "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
+- "devOptional": true,
++ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+@@ -11643,7 +14152,7 @@
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-to-typescript-lite": {
+@@ -11661,14 +14170,14 @@
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+@@ -11695,6 +14204,15 @@
+ "graceful-fs": "^4.1.6"
+ }
+ },
++ "node_modules/jsonpointer": {
++ "version": "5.0.1",
++ "resolved": "https://registry.npmmirror.com/jsonpointer/-/jsonpointer-5.0.1.tgz",
++ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=0.10.0"
++ }
++ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+@@ -11789,7 +14307,7 @@
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+@@ -11848,7 +14366,6 @@
+ "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.4.tgz",
+ "integrity": "sha512-3An0GCLDSR34tsCO4H8Tef8Pp2ngtaZDAZnsWJYelqXUK5wyiHvGItgK/xcSkmHLSTn1Jcho1mRQs2ehRzvKKw==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "accepts": "^1.3.5",
+ "cache-content-type": "^1.0.0",
+@@ -12041,11 +14558,20 @@
+ "node": ">= 0.6.3"
+ }
+ },
++ "node_modules/leven": {
++ "version": "3.1.0",
++ "resolved": "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz",
++ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6"
++ }
++ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+@@ -12069,7 +14595,6 @@
+ "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "license": "MPL-2.0",
+- "peer": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+@@ -12442,6 +14967,12 @@
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "license": "MIT"
+ },
++ "node_modules/lodash.debounce": {
++ "version": "4.0.8",
++ "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
++ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
++ "license": "MIT"
++ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+@@ -12482,7 +15013,7 @@
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+@@ -12770,6 +15301,12 @@
+ "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
+ "license": "MIT"
+ },
++ "node_modules/module-details-from-path": {
++ "version": "1.0.4",
++ "resolved": "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
++ "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
++ "license": "MIT"
++ },
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
+@@ -12886,7 +15423,7 @@
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+@@ -13657,7 +16194,6 @@
+ "resolved": "https://registry.npmmirror.com/nuxt/-/nuxt-4.5.2.tgz",
+ "integrity": "sha512-tR3fcqeHlHmmkLMpIg3V7Y+1ltr302lW8djMw/iy+myfo7QSSz+BVJDuQhg5j73b9oteSyBfOKTDYTgvMtj6TA==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@dxup/nuxt": "^0.5.6",
+ "@nuxt/cli": "^3.37.0",
+@@ -13796,7 +16332,6 @@
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-1.2.3.tgz",
+ "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+@@ -13963,6 +16498,35 @@
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
++ "node_modules/object-keys": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
++ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
++ "node_modules/object.assign": {
++ "version": "4.1.7",
++ "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz",
++ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.8",
++ "call-bound": "^1.0.3",
++ "define-properties": "^1.2.1",
++ "es-object-atoms": "^1.0.0",
++ "has-symbols": "^1.1.0",
++ "object-keys": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/obug": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/obug/-/obug-2.1.4.tgz",
+@@ -14082,9 +16646,8 @@
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+@@ -14108,6 +16671,24 @@
+ "@otplib/preset-v11": "^12.0.1"
+ }
+ },
++ "node_modules/own-keys": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.2.tgz",
++ "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.4",
++ "get-intrinsic": "^1.3.0",
++ "object-keys": "^1.1.1",
++ "safe-push-apply": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/oxc-walker": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/oxc-walker/-/oxc-walker-1.1.1.tgz",
+@@ -14223,7 +16804,7 @@
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+@@ -14457,6 +17038,15 @@
+ "node": ">= 10.12"
+ }
+ },
++ "node_modules/possible-typed-array-names": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
++ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
+ "node_modules/postcss": {
+ "version": "8.5.28",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.28.tgz",
+@@ -14476,7 +17066,6 @@
+ }
+ ],
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.18",
+ "picocolors": "^1.1.1",
+@@ -15022,7 +17611,6 @@
+ "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz",
+ "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+@@ -15073,7 +17661,6 @@
+ "resolved": "https://registry.npmmirror.com/postgres/-/postgres-3.4.9.tgz",
+ "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==",
+ "license": "Unlicense",
+- "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+@@ -15137,7 +17724,7 @@
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+@@ -15229,7 +17816,7 @@
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+@@ -15515,6 +18102,46 @@
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "license": "Apache-2.0"
+ },
++ "node_modules/reflect.getprototypeof": {
++ "version": "1.0.10",
++ "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
++ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.8",
++ "define-properties": "^1.2.1",
++ "es-abstract": "^1.23.9",
++ "es-errors": "^1.3.0",
++ "es-object-atoms": "^1.0.0",
++ "get-intrinsic": "^1.2.7",
++ "get-proto": "^1.0.1",
++ "which-builtin-type": "^1.2.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/regenerate": {
++ "version": "1.4.2",
++ "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz",
++ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
++ "license": "MIT"
++ },
++ "node_modules/regenerate-unicode-properties": {
++ "version": "10.2.2",
++ "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
++ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
++ "license": "MIT",
++ "dependencies": {
++ "regenerate": "^1.4.2"
++ },
++ "engines": {
++ "node": ">=4"
++ }
++ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+@@ -15533,14 +18160,56 @@
+ "refa": "^0.12.1"
+ },
+ "engines": {
+- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
++ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
++ }
++ },
++ "node_modules/regexp.prototype.flags": {
++ "version": "1.5.4",
++ "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
++ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.8",
++ "define-properties": "^1.2.1",
++ "es-errors": "^1.3.0",
++ "get-proto": "^1.0.1",
++ "gopd": "^1.2.0",
++ "set-function-name": "^2.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/regexpu-core": {
++ "version": "6.4.0",
++ "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz",
++ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
++ "license": "MIT",
++ "dependencies": {
++ "regenerate": "^1.4.2",
++ "regenerate-unicode-properties": "^10.2.2",
++ "regjsgen": "^0.8.0",
++ "regjsparser": "^0.13.0",
++ "unicode-match-property-ecmascript": "^2.0.0",
++ "unicode-match-property-value-ecmascript": "^2.2.1"
++ },
++ "engines": {
++ "node": ">=4"
+ }
+ },
++ "node_modules/regjsgen": {
++ "version": "0.8.0",
++ "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz",
++ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
++ "license": "MIT"
++ },
+ "node_modules/regjsparser": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.2.tgz",
+ "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==",
+- "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~3.1.0"
+@@ -15719,6 +18388,28 @@
+ "node": ">=0.10.0"
+ }
+ },
++ "node_modules/require-from-string": {
++ "version": "2.0.2",
++ "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
++ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=0.10.0"
++ }
++ },
++ "node_modules/require-in-the-middle": {
++ "version": "8.0.1",
++ "resolved": "https://registry.npmmirror.com/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz",
++ "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==",
++ "license": "MIT",
++ "dependencies": {
++ "debug": "^4.3.5",
++ "module-details-from-path": "^1.0.3"
++ },
++ "engines": {
++ "node": ">=9.3.0 || >=8.10.0 <9.0.0"
++ }
++ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+@@ -15762,7 +18453,7 @@
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+@@ -15869,7 +18560,6 @@
+ "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.2.7.tgz",
+ "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@oxc-project/types": "=0.148.0",
+ "@rolldown/pluginutils": "^1.0.0"
+@@ -15935,7 +18625,6 @@
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.1.tgz",
+ "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+@@ -16217,6 +18906,31 @@
+ "queue-microtask": "^1.2.2"
+ }
+ },
++ "node_modules/safe-array-concat": {
++ "version": "1.1.4",
++ "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
++ "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "get-intrinsic": "^1.3.0",
++ "has-symbols": "^1.1.0",
++ "isarray": "^2.0.5"
++ },
++ "engines": {
++ "node": ">=0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/safe-array-concat/node_modules/isarray": {
++ "version": "2.0.5",
++ "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz",
++ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
++ "license": "MIT"
++ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+@@ -16237,6 +18951,28 @@
+ ],
+ "license": "MIT"
+ },
++ "node_modules/safe-push-apply": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
++ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
++ "license": "MIT",
++ "dependencies": {
++ "es-errors": "^1.3.0",
++ "isarray": "^2.0.5"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/safe-push-apply/node_modules/isarray": {
++ "version": "2.0.5",
++ "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz",
++ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
++ "license": "MIT"
++ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+@@ -16447,6 +19183,52 @@
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "license": "ISC"
+ },
++ "node_modules/set-function-length": {
++ "version": "1.2.2",
++ "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
++ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
++ "license": "MIT",
++ "dependencies": {
++ "define-data-property": "^1.1.4",
++ "es-errors": "^1.3.0",
++ "function-bind": "^1.1.2",
++ "get-intrinsic": "^1.2.4",
++ "gopd": "^1.0.1",
++ "has-property-descriptors": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
++ "node_modules/set-function-name": {
++ "version": "2.0.2",
++ "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz",
++ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
++ "license": "MIT",
++ "dependencies": {
++ "define-data-property": "^1.1.4",
++ "es-errors": "^1.3.0",
++ "functions-have-names": "^1.2.3",
++ "has-property-descriptors": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
++ "node_modules/set-proto": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz",
++ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
++ "license": "MIT",
++ "dependencies": {
++ "dunder-proto": "^1.0.1",
++ "es-errors": "^1.3.0",
++ "es-object-atoms": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+@@ -16493,14 +19275,14 @@
+ }
+ },
+ "node_modules/side-channel": {
+- "version": "1.1.0",
+- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.1.tgz",
++ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+- "object-inspect": "^1.13.3",
+- "side-channel-list": "^1.0.0",
++ "object-inspect": "^1.13.4",
++ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+@@ -16512,13 +19294,13 @@
+ }
+ },
+ "node_modules/side-channel-list": {
+- "version": "1.0.0",
+- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
++ "version": "1.0.1",
++ "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz",
++ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+- "object-inspect": "^1.13.3"
++ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+@@ -16767,6 +19549,19 @@
+ "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
+ "license": "MIT"
+ },
++ "node_modules/stop-iteration-iterator": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
++ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
++ "license": "MIT",
++ "dependencies": {
++ "es-errors": "^1.3.0",
++ "internal-slot": "^1.1.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+@@ -16830,6 +19625,104 @@
+ "node": ">=8"
+ }
+ },
++ "node_modules/string.prototype.matchall": {
++ "version": "4.1.0",
++ "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz",
++ "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "define-properties": "^1.2.1",
++ "es-abstract": "^1.24.2",
++ "es-errors": "^1.3.0",
++ "es-object-atoms": "^1.1.2",
++ "get-intrinsic": "^1.3.0",
++ "gopd": "^1.2.0",
++ "has-symbols": "^1.1.0",
++ "internal-slot": "^1.1.0",
++ "regexp.prototype.flags": "^1.5.4",
++ "set-function-name": "^2.0.2",
++ "side-channel": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/string.prototype.trim": {
++ "version": "1.2.11",
++ "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",
++ "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "define-data-property": "^1.1.4",
++ "define-properties": "^1.2.1",
++ "es-abstract": "^1.24.2",
++ "es-object-atoms": "^1.1.2",
++ "has-property-descriptors": "^1.0.2",
++ "safe-regex-test": "^1.1.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/string.prototype.trimend": {
++ "version": "1.0.10",
++ "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",
++ "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "define-properties": "^1.2.1",
++ "es-object-atoms": "^1.1.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/string.prototype.trimstart": {
++ "version": "1.0.8",
++ "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
++ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.7",
++ "define-properties": "^1.2.1",
++ "es-object-atoms": "^1.0.0"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/stringify-object": {
++ "version": "3.3.0",
++ "resolved": "https://registry.npmmirror.com/stringify-object/-/stringify-object-3.3.0.tgz",
++ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
++ "license": "BSD-2-Clause",
++ "dependencies": {
++ "get-own-enumerable-property-symbols": "^3.0.0",
++ "is-obj": "^1.0.1",
++ "is-regexp": "^1.0.0"
++ },
++ "engines": {
++ "node": ">=4"
++ }
++ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+@@ -16855,6 +19748,15 @@
+ "node": ">=8"
+ }
+ },
++ "node_modules/strip-comments": {
++ "version": "2.0.1",
++ "resolved": "https://registry.npmmirror.com/strip-comments/-/strip-comments-2.0.1.tgz",
++ "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=10"
++ }
++ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+@@ -16884,7 +19786,7 @@
+ "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==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+@@ -17142,7 +20044,6 @@
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+@@ -17243,6 +20144,57 @@
+ "streamx": "^2.12.5"
+ }
+ },
++ "node_modules/temp-dir": {
++ "version": "2.0.0",
++ "resolved": "https://registry.npmmirror.com/temp-dir/-/temp-dir-2.0.0.tgz",
++ "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=8"
++ }
++ },
++ "node_modules/tempy": {
++ "version": "0.6.0",
++ "resolved": "https://registry.npmmirror.com/tempy/-/tempy-0.6.0.tgz",
++ "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==",
++ "license": "MIT",
++ "dependencies": {
++ "is-stream": "^2.0.0",
++ "temp-dir": "^2.0.0",
++ "type-fest": "^0.16.0",
++ "unique-string": "^2.0.0"
++ },
++ "engines": {
++ "node": ">=10"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/sindresorhus"
++ }
++ },
++ "node_modules/tempy/node_modules/is-stream": {
++ "version": "2.0.1",
++ "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
++ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=8"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/sindresorhus"
++ }
++ },
++ "node_modules/tempy/node_modules/type-fest": {
++ "version": "0.16.0",
++ "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.16.0.tgz",
++ "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
++ "license": "(MIT OR CC0-1.0)",
++ "engines": {
++ "node": ">=10"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/sindresorhus"
++ }
++ },
+ "node_modules/terser": {
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+@@ -17469,7 +20421,6 @@
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+@@ -17506,7 +20457,7 @@
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+@@ -17543,6 +20494,80 @@
+ "node": ">= 0.6"
+ }
+ },
++ "node_modules/typed-array-buffer": {
++ "version": "1.0.3",
++ "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
++ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "es-errors": "^1.3.0",
++ "is-typed-array": "^1.1.14"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ }
++ },
++ "node_modules/typed-array-byte-length": {
++ "version": "1.0.3",
++ "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
++ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.8",
++ "for-each": "^0.3.3",
++ "gopd": "^1.2.0",
++ "has-proto": "^1.2.0",
++ "is-typed-array": "^1.1.14"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/typed-array-byte-offset": {
++ "version": "1.0.4",
++ "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
++ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
++ "license": "MIT",
++ "dependencies": {
++ "available-typed-arrays": "^1.0.7",
++ "call-bind": "^1.0.8",
++ "for-each": "^0.3.3",
++ "gopd": "^1.2.0",
++ "has-proto": "^1.2.0",
++ "is-typed-array": "^1.1.15",
++ "reflect.getprototypeof": "^1.0.9"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/typed-array-length": {
++ "version": "1.0.8",
++ "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.8.tgz",
++ "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bind": "^1.0.9",
++ "for-each": "^0.3.5",
++ "gopd": "^1.2.0",
++ "is-typed-array": "^1.1.15",
++ "possible-typed-array-names": "^1.1.0",
++ "reflect.getprototypeof": "^1.0.10"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+@@ -17553,9 +20578,8 @@
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+- "devOptional": true,
++ "dev": true,
+ "license": "Apache-2.0",
+- "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+@@ -17576,6 +20600,24 @@
+ "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==",
+ "license": "MIT"
+ },
++ "node_modules/unbox-primitive": {
++ "version": "1.1.0",
++ "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
++ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.3",
++ "has-bigints": "^1.0.2",
++ "has-symbols": "^1.1.0",
++ "which-boxed-primitive": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/uncrypto": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
+@@ -17647,7 +20689,6 @@
+ "resolved": "https://registry.npmmirror.com/unhead/-/unhead-3.4.0.tgz",
+ "integrity": "sha512-OlgpyYjY+NkFyQCaMihg2uTFbbvyr5lZr6TokS68VdFTXJ5jo4JwFtGR8PwKy/wRpjbQYgKF7ZYO+pbRgxtSmw==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "hookable": "^6.1.1",
+ "unplugin": "^3.3.0"
+@@ -17664,6 +20705,46 @@
+ }
+ }
+ },
++ "node_modules/unicode-canonical-property-names-ecmascript": {
++ "version": "2.0.1",
++ "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
++ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=4"
++ }
++ },
++ "node_modules/unicode-match-property-ecmascript": {
++ "version": "2.0.0",
++ "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
++ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
++ "license": "MIT",
++ "dependencies": {
++ "unicode-canonical-property-names-ecmascript": "^2.0.0",
++ "unicode-property-aliases-ecmascript": "^2.0.0"
++ },
++ "engines": {
++ "node": ">=4"
++ }
++ },
++ "node_modules/unicode-match-property-value-ecmascript": {
++ "version": "2.2.1",
++ "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
++ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=4"
++ }
++ },
++ "node_modules/unicode-property-aliases-ecmascript": {
++ "version": "2.2.0",
++ "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
++ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=4"
++ }
++ },
+ "node_modules/unicorn-magic": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+@@ -17743,6 +20824,18 @@
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
++ "node_modules/unique-string": {
++ "version": "2.0.0",
++ "resolved": "https://registry.npmmirror.com/unique-string/-/unique-string-2.0.0.tgz",
++ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
++ "license": "MIT",
++ "dependencies": {
++ "crypto-random-string": "^2.0.0"
++ },
++ "engines": {
++ "node": ">=8"
++ }
++ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+@@ -17851,7 +20944,6 @@
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "napi-postinstall": "^0.3.4"
+ },
+@@ -18055,6 +21147,16 @@
+ "pkg-types": "^2.3.0"
+ }
+ },
++ "node_modules/upath": {
++ "version": "1.2.0",
++ "resolved": "https://registry.npmmirror.com/upath/-/upath-1.2.0.tgz",
++ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=4",
++ "yarn": "*"
++ }
++ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+@@ -18095,7 +21197,7 @@
+ "version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+- "devOptional": true,
++ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+@@ -18181,7 +21283,6 @@
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.2.tgz",
+ "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "lightningcss": "^1.33.0",
+ "picomatch": "^4.0.5",
+@@ -18478,6 +21579,48 @@
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
++ "node_modules/vite-plugin-pwa": {
++ "version": "1.3.0",
++ "resolved": "https://registry.npmmirror.com/vite-plugin-pwa/-/vite-plugin-pwa-1.3.0.tgz",
++ "integrity": "sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==",
++ "license": "MIT",
++ "dependencies": {
++ "debug": "^4.3.6",
++ "pretty-bytes": "^6.1.1",
++ "tinyglobby": "^0.2.10",
++ "workbox-build": "^7.4.1",
++ "workbox-window": "^7.4.1"
++ },
++ "engines": {
++ "node": ">=16.0.0"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/antfu"
++ },
++ "peerDependencies": {
++ "@vite-pwa/assets-generator": "^1.0.0",
++ "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
++ "workbox-build": "^7.4.1",
++ "workbox-window": "^7.4.1"
++ },
++ "peerDependenciesMeta": {
++ "@vite-pwa/assets-generator": {
++ "optional": true
++ }
++ }
++ },
++ "node_modules/vite-plugin-pwa/node_modules/pretty-bytes": {
++ "version": "6.1.1",
++ "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
++ "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
++ "license": "MIT",
++ "engines": {
++ "node": "^14.13.1 || >=16.0.0"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/sindresorhus"
++ }
++ },
+ "node_modules/vite-plugin-top-level-await": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz",
+@@ -18545,7 +21688,6 @@
+ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.42.tgz",
+ "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.42",
+ "@vue/compiler-sfc": "3.5.42",
+@@ -18589,7 +21731,6 @@
+ "integrity": "sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==",
+ "dev": true,
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "debug": "^4.4.0",
+ "eslint-scope": "^8.2.0 || ^9.0.0",
+@@ -18613,7 +21754,6 @@
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+ "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+ "license": "MIT",
+- "peer": true,
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+@@ -18680,12 +21820,103 @@
+ "node": ">= 8"
+ }
+ },
++ "node_modules/which-boxed-primitive": {
++ "version": "1.1.1",
++ "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
++ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
++ "license": "MIT",
++ "dependencies": {
++ "is-bigint": "^1.1.0",
++ "is-boolean-object": "^1.2.1",
++ "is-number-object": "^1.1.1",
++ "is-string": "^1.1.1",
++ "is-symbol": "^1.1.1"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/which-builtin-type": {
++ "version": "1.2.1",
++ "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
++ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
++ "license": "MIT",
++ "dependencies": {
++ "call-bound": "^1.0.2",
++ "function.prototype.name": "^1.1.6",
++ "has-tostringtag": "^1.0.2",
++ "is-async-function": "^2.0.0",
++ "is-date-object": "^1.1.0",
++ "is-finalizationregistry": "^1.1.0",
++ "is-generator-function": "^1.0.10",
++ "is-regex": "^1.2.1",
++ "is-weakref": "^1.0.2",
++ "isarray": "^2.0.5",
++ "which-boxed-primitive": "^1.1.0",
++ "which-collection": "^1.0.2",
++ "which-typed-array": "^1.1.16"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
++ "node_modules/which-builtin-type/node_modules/isarray": {
++ "version": "2.0.5",
++ "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz",
++ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
++ "license": "MIT"
++ },
++ "node_modules/which-collection": {
++ "version": "1.0.2",
++ "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz",
++ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
++ "license": "MIT",
++ "dependencies": {
++ "is-map": "^2.0.3",
++ "is-set": "^2.0.3",
++ "is-weakmap": "^2.0.2",
++ "is-weakset": "^2.0.3"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/which-module": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+ "license": "ISC"
+ },
++ "node_modules/which-typed-array": {
++ "version": "1.1.22",
++ "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.22.tgz",
++ "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
++ "license": "MIT",
++ "dependencies": {
++ "available-typed-arrays": "^1.0.7",
++ "call-bind": "^1.0.9",
++ "call-bound": "^1.0.4",
++ "for-each": "^0.3.5",
++ "get-proto": "^1.0.1",
++ "gopd": "^1.2.0",
++ "has-tostringtag": "^1.0.2"
++ },
++ "engines": {
++ "node": ">= 0.4"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/ljharb"
++ }
++ },
+ "node_modules/wmf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+@@ -18708,12 +21939,309 @@
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
++ "node_modules/workbox-background-sync": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-background-sync/-/workbox-background-sync-7.4.1.tgz",
++ "integrity": "sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==",
++ "license": "MIT",
++ "dependencies": {
++ "idb": "^7.0.1",
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-broadcast-update": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz",
++ "integrity": "sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-build": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-build/-/workbox-build-7.4.1.tgz",
++ "integrity": "sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==",
++ "license": "MIT",
++ "dependencies": {
++ "@apideck/better-ajv-errors": "^0.3.1",
++ "@babel/core": "^7.24.4",
++ "@babel/preset-env": "^7.11.0",
++ "@babel/runtime": "^7.11.2",
++ "@rollup/plugin-babel": "^6.1.0",
++ "@rollup/plugin-node-resolve": "^16.0.3",
++ "@rollup/plugin-replace": "^6.0.3",
++ "@rollup/plugin-terser": "^1.0.0",
++ "@trickfilm400/rollup-plugin-off-main-thread": "^3.0.0-pre1",
++ "ajv": "^8.6.0",
++ "common-tags": "^1.8.0",
++ "eta": "^4.5.1",
++ "fast-json-stable-stringify": "^2.1.0",
++ "fs-extra": "^9.0.1",
++ "glob": "^11.0.1",
++ "pretty-bytes": "^5.3.0",
++ "rollup": "^4.53.3",
++ "source-map": "^0.8.0-beta.0",
++ "stringify-object": "^3.3.0",
++ "strip-comments": "^2.0.1",
++ "tempy": "^0.6.0",
++ "upath": "^1.2.0",
++ "workbox-background-sync": "7.4.1",
++ "workbox-broadcast-update": "7.4.1",
++ "workbox-cacheable-response": "7.4.1",
++ "workbox-core": "7.4.1",
++ "workbox-expiration": "7.4.1",
++ "workbox-google-analytics": "7.4.1",
++ "workbox-navigation-preload": "7.4.1",
++ "workbox-precaching": "7.4.1",
++ "workbox-range-requests": "7.4.1",
++ "workbox-recipes": "7.4.1",
++ "workbox-routing": "7.4.1",
++ "workbox-strategies": "7.4.1",
++ "workbox-streams": "7.4.1",
++ "workbox-sw": "7.4.1",
++ "workbox-window": "7.4.1"
++ },
++ "engines": {
++ "node": ">=20.0.0"
++ }
++ },
++ "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": {
++ "version": "0.3.7",
++ "resolved": "https://registry.npmmirror.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz",
++ "integrity": "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==",
++ "license": "MIT",
++ "dependencies": {
++ "jsonpointer": "^5.0.1",
++ "leven": "^3.1.0"
++ },
++ "engines": {
++ "node": ">=10"
++ },
++ "peerDependencies": {
++ "ajv": ">=8"
++ }
++ },
++ "node_modules/workbox-build/node_modules/@isaacs/cliui": {
++ "version": "9.0.0",
++ "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-9.0.0.tgz",
++ "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
++ "license": "BlueOak-1.0.0",
++ "engines": {
++ "node": ">=18"
++ }
++ },
++ "node_modules/workbox-build/node_modules/ajv": {
++ "version": "8.20.0",
++ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.20.0.tgz",
++ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
++ "license": "MIT",
++ "dependencies": {
++ "fast-deep-equal": "^3.1.3",
++ "fast-uri": "^3.0.1",
++ "json-schema-traverse": "^1.0.0",
++ "require-from-string": "^2.0.2"
++ },
++ "funding": {
++ "type": "github",
++ "url": "https://github.com/sponsors/epoberezkin"
++ }
++ },
++ "node_modules/workbox-build/node_modules/glob": {
++ "version": "11.1.0",
++ "resolved": "https://registry.npmmirror.com/glob/-/glob-11.1.0.tgz",
++ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
++ "license": "BlueOak-1.0.0",
++ "dependencies": {
++ "foreground-child": "^3.3.1",
++ "jackspeak": "^4.1.1",
++ "minimatch": "^10.1.1",
++ "minipass": "^7.1.2",
++ "package-json-from-dist": "^1.0.0",
++ "path-scurry": "^2.0.0"
++ },
++ "bin": {
++ "glob": "dist/esm/bin.mjs"
++ },
++ "engines": {
++ "node": "20 || >=22"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/isaacs"
++ }
++ },
++ "node_modules/workbox-build/node_modules/jackspeak": {
++ "version": "4.2.3",
++ "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-4.2.3.tgz",
++ "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
++ "license": "BlueOak-1.0.0",
++ "dependencies": {
++ "@isaacs/cliui": "^9.0.0"
++ },
++ "engines": {
++ "node": "20 || >=22"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/isaacs"
++ }
++ },
++ "node_modules/workbox-build/node_modules/json-schema-traverse": {
++ "version": "1.0.0",
++ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
++ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
++ "license": "MIT"
++ },
++ "node_modules/workbox-build/node_modules/pretty-bytes": {
++ "version": "5.6.0",
++ "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
++ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
++ "license": "MIT",
++ "engines": {
++ "node": ">=6"
++ },
++ "funding": {
++ "url": "https://github.com/sponsors/sindresorhus"
++ }
++ },
++ "node_modules/workbox-build/node_modules/source-map": {
++ "version": "0.8.0",
++ "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.8.0.tgz",
++ "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==",
++ "license": "BSD-3-Clause",
++ "engines": {
++ "node": ">= 12"
++ }
++ },
++ "node_modules/workbox-cacheable-response": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-cacheable-response/-/workbox-cacheable-response-7.4.1.tgz",
++ "integrity": "sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-core": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-core/-/workbox-core-7.4.1.tgz",
++ "integrity": "sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==",
++ "license": "MIT"
++ },
++ "node_modules/workbox-expiration": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-expiration/-/workbox-expiration-7.4.1.tgz",
++ "integrity": "sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==",
++ "license": "MIT",
++ "dependencies": {
++ "idb": "^7.0.1",
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-google-analytics": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-google-analytics/-/workbox-google-analytics-7.4.1.tgz",
++ "integrity": "sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-background-sync": "7.4.1",
++ "workbox-core": "7.4.1",
++ "workbox-routing": "7.4.1",
++ "workbox-strategies": "7.4.1"
++ }
++ },
++ "node_modules/workbox-navigation-preload": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-navigation-preload/-/workbox-navigation-preload-7.4.1.tgz",
++ "integrity": "sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-precaching": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-precaching/-/workbox-precaching-7.4.1.tgz",
++ "integrity": "sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1",
++ "workbox-routing": "7.4.1",
++ "workbox-strategies": "7.4.1"
++ }
++ },
++ "node_modules/workbox-range-requests": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-range-requests/-/workbox-range-requests-7.4.1.tgz",
++ "integrity": "sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-recipes": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-recipes/-/workbox-recipes-7.4.1.tgz",
++ "integrity": "sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-cacheable-response": "7.4.1",
++ "workbox-core": "7.4.1",
++ "workbox-expiration": "7.4.1",
++ "workbox-precaching": "7.4.1",
++ "workbox-routing": "7.4.1",
++ "workbox-strategies": "7.4.1"
++ }
++ },
++ "node_modules/workbox-routing": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-routing/-/workbox-routing-7.4.1.tgz",
++ "integrity": "sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-strategies": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-strategies/-/workbox-strategies-7.4.1.tgz",
++ "integrity": "sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1"
++ }
++ },
++ "node_modules/workbox-streams": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-streams/-/workbox-streams-7.4.1.tgz",
++ "integrity": "sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==",
++ "license": "MIT",
++ "dependencies": {
++ "workbox-core": "7.4.1",
++ "workbox-routing": "7.4.1"
++ }
++ },
++ "node_modules/workbox-sw": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-sw/-/workbox-sw-7.4.1.tgz",
++ "integrity": "sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==",
++ "license": "MIT"
++ },
++ "node_modules/workbox-window": {
++ "version": "7.4.1",
++ "resolved": "https://registry.npmmirror.com/workbox-window/-/workbox-window-7.4.1.tgz",
++ "integrity": "sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==",
++ "license": "MIT",
++ "dependencies": {
++ "@types/trusted-types": "^2.0.2",
++ "workbox-core": "7.4.1"
++ }
++ },
+ "node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+@@ -18901,9 +22429,8 @@
+ "version": "2.9.0",
+ "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+- "devOptional": true,
++ "dev": true,
+ "license": "ISC",
+- "peer": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+diff --git a/package.json b/package.json
+index d371580..cf0977c 100644
+--- a/package.json
++++ b/package.json
+@@ -50,7 +50,8 @@
+ "@pixi/filter-bulge-pinch": "^5.1.1",
+ "@pixi/filter-color-matrix": "^7.4.3",
+ "@pixi/sprite": "^7.4.3",
+- "@sansenjian/qq-music-api": "2.6.0",
++ "@sentry/node": "^10.73.0",
++ "@sentry/vue": "^10.73.0",
+ "@simplewebauthn/browser": "^13.3.0",
+ "@simplewebauthn/server": "^13.3.3",
+ "@types/crypto-js": "^4.2.2",
+@@ -61,6 +62,7 @@
+ "@types/pg": "^8.20.0",
+ "@ungap/structured-clone": "^1.3.0",
+ "@vercel/speed-insights": "^1.3.1",
++ "@vite-pwa/nuxt": "^1.1.1",
+ "axios": "^1.20.0",
+ "bcryptjs": "^2.4.3",
+ "bezier-easing": "^2.1.0",
+@@ -106,6 +108,7 @@
+ "node": ">=22.11.0"
+ },
+ "devDependencies": {
++ "@clack/prompts": "^1.1.0",
+ "@nuxt/eslint": "^1.17.0",
+ "eslint": "^9.39.5",
+ "prettier": "^3.9.6",
+diff --git a/scripts/deploy.js b/scripts/deploy.js
+index c20219f..9963ca4 100644
+--- a/scripts/deploy.js
++++ b/scripts/deploy.js
+@@ -106,26 +106,26 @@ async function deploy() {
+ logStep('馃摝', '瀹夎渚濊禆...')
+ let installed = false
+
+- // 浼樺厛灏濊瘯 pnpm install --frozen-lockfile
+- if (fileExists('pnpm-lock.yaml')) {
+- log('灏濊瘯浣跨敤 pnpm install --frozen-lockfile 瀹夎...', 'cyan')
+- if (safeExec('pnpm install --frozen-lockfile')) {
++ // 浼樺厛浣跨敤 npm锛堢敤鎴疯姹傦細绂佹 pnpm锛夆€斺€?浼樺厛 package-lock.json 鍐荤粨瀹夎
++ if (fileExists('package-lock.json')) {
++ log('灏濊瘯浣跨敤 npm ci锛堝熀浜?package-lock.json 鍐荤粨瀹夎锛?..', 'cyan')
++ if (safeExec('npm ci --no-audit --no-fund')) {
+ installed = true
+- logSuccess('渚濊禆瀹夎瀹屾垚 (pnpm install --frozen-lockfile)')
++ logSuccess('渚濊禆瀹夎瀹屾垚 (npm ci)')
+ } else {
+- logWarning('pnpm install --frozen-lockfile 瀹夎澶辫触锛屽噯澶囧洖閫€鍒?pnpm install...')
++ logWarning('npm ci 澶辫触锛屽噯澶囧洖閫€鍒?npm install...')
+ }
+ } else {
+- logWarning('鏈娴嬪埌 pnpm-lock.yaml锛岃烦杩囧喕缁撻攣鏂囦欢瀹夎锛岀洿鎺ヤ娇鐢?pnpm install...')
++ logWarning('鏈娴嬪埌 package-lock.json锛岀洿鎺ヤ娇鐢?npm install...')
+ }
+
+- // 濡傛灉鍐荤粨閿佹枃浠跺畨瑁呮病杩愯鎴栧け璐ワ紝浣跨敤 pnpm install
++ // 鍏滃簳鐢?npm install
+ if (!installed) {
+- log('姝e湪浣跨敤 pnpm install 瀹夎...', 'cyan')
+- if (!safeExec('pnpm install')) {
++ log('姝e湪浣跨敤 npm install 瀹夎...', 'cyan')
++ if (!safeExec('npm install --no-audit --no-fund')) {
+ throw new Error('渚濊禆瀹夎澶辫触')
+ }
+- logSuccess('渚濊禆瀹夎瀹屾垚 (pnpm install)')
++ logSuccess('渚濊禆瀹夎瀹屾垚 (npm install)')
+ }
+ }
+
+@@ -166,7 +166,10 @@ async function deploy() {
+ // 4. 鍒涘缓绠$悊鍛樿处鎴? if (fileExists('scripts/create-admin.js') && dbSyncSuccess) {
+ logStep('馃懁', '妫€鏌ョ鐞嗗憳璐︽埛...')
+- safeExec('pnpm run create-admin')
++ // 浼樺厛 npm run create-admin锛屽厹搴曠洿鎺ョ敤 node_modules/.bin/tsx 璋冪敤锛堢敤鎴风鐢?pnpm锛?+ if (!safeExec('npm run create-admin')) {
++ safeExec('./node_modules/.bin/tsx scripts/create-admin.js')
++ }
+ }
+
+ // 5. 鏋勫缓搴旂敤
+@@ -178,7 +181,8 @@ async function deploy() {
+ ...process.env,
+ NODE_OPTIONS: process.env.NODE_OPTIONS || DEFAULT_NODE_OPTIONS
+ }
+- if (!safeExec('pnpm exec nuxt build', { env: buildEnv })) {
++ // 鐢?npm run build锛? nuxt build锛夛紝閬垮厤鐢?pnpm exec
++ if (!safeExec('npm run build', { env: buildEnv })) {
+ throw new Error('搴旂敤鏋勫缓澶辫触')
+ }
+ logSuccess('搴旂敤鏋勫缓瀹屾垚')
+diff --git a/scripts/setup-deploy-user.sh b/scripts/setup-deploy-user.sh
+new file mode 100644
+index 0000000..dc9a48b
+--- /dev/null
++++ b/scripts/setup-deploy-user.sh
+@@ -0,0 +1,168 @@
++#!/usr/bin/env bash
++# ============================================================================
++# setup-deploy-user.sh
++# 鍦ㄣ€岀敓浜ф湇鍔″櫒銆嶄笂涓€閿仛濂?CI 鑷姩閮ㄧ讲鎵€闇€鐨勭郴缁熼厤缃細
++# 1) 鏂板缓 deploy 鐢ㄦ埛锛堜綆鏉冮檺锛岀姝㈠瘑鐮佺櫥褰曪紝鍙兘 key 鐧诲綍锛?+# 2) 鐢熸垚 ed25519 SSH 瀵嗛挜瀵癸紙绉侀挜浣犵矘鍘?Gitea Secrets DEPLOY_SSH_KEY锛?+# 3) 鍔犲叆 docker 缁勶紝鍏佽 docker compose 鎿嶄綔
++# 4) 缁?/opt/byw-voicehub 鐩綍璇诲啓鏉冮檺锛坰etfacl锛?+# 5) 鎵撳嵃绉侀挜鍐呭 & 鍏挜鎸囩汗锛屼究浜庝綘妫€鏌ュ拰澶嶅埗
++#
++# 鐢ㄦ硶锛堝湪鐢熶骇鏈嶅姟鍣ㄤ笂锛宻udo 杩愯锛夛細
++# sudo bash scripts/setup-deploy-user.sh [--deploy-dir /opt/byw-voicehub]
++# ============================================================================
++set -euo pipefail
++
++RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[1;36m'; NC='\033[0m'
++info(){ echo -e "${GREEN}[INFO]${NC} $*"; }
++warn(){ echo -e "${YELLOW}[WARN]${NC} $*"; }
++err(){ echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
++ok(){ echo -e "${CYAN}[ OK ]${NC} $*"; }
++
++DEPLOY_DIR="/opt/byw-voicehub"
++DEPLOY_USER="deploy"
++while [[ $# -gt 0 ]]; do
++ case "$1" in
++ --deploy-dir) DEPLOY_DIR="$2"; shift 2 ;;
++ --user) DEPLOY_USER="$2"; shift 2 ;;
++ -h|--help)
++ sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
++ exit 0
++ ;;
++ *) err "鏈煡鍙傛暟 $1锛?-help 鏌ョ湅甯姪锛? ;;
++ esac
++done
++
++[[ $EUID -eq 0 ]] || err "璇风敤 sudo 杩愯鏈剼鏈?
++command -v docker >/dev/null 2>&1 || err "鍏堣瀹?Docker 鍐嶈窇鏈剼鏈紙璺緞涓€鐨勬暀绋嬮噷鏈夛級"
++command -v setfacl >/dev/null 2>&1 || {
++ warn "娌¤ acl 宸ュ叿锛岀幇鍦?apt install"
++ apt-get update && apt-get install -y acl
++}
++
++# -------- 1. 寤虹敤鎴?--------
++if ! id -u "${DEPLOY_USER}" >/dev/null 2>&1; then
++ # 娉ㄦ剰锛歎buntu 22.04/24.04 鐨?useradd 娌℃湁 --disabled-password 閫夐」
++ # 锛堥偅鏄?adduser 鍛戒护鐨勫弬鏁帮紝涔嬪墠鑴氭湰鍐欓敊浜嗗懡浠わ級銆?+ # 杩欓噷鐢?useradd 鍘熺敓鍙傛暟锛?+ # -m = 鍒涘缓 home
++ # -s = 鎸囧畾鐧诲綍 shell
++ # -p '*' = 瀵嗙爜璁剧疆涓恒€?銆嶏紙閿佸畾锛岀姝㈢敤瀵嗙爜鐧诲綍锛屽彧鑳?SSH 瀵嗛挜锛?+ # 杩欐牱鍜屼箣鍓嶃€屽彧鍏佽 SSH 瀵嗛挜鐧诲綍銆佹棤瀵嗙爜銆嶇殑鎰忓浘瀹屽叏涓€鑷淬€?+ useradd -m -s /usr/bin/bash -p '*' "${DEPLOY_USER}"
++ # Ubuntu/Debian 棰濆淇濋櫓锛氶攣瀹氬瘑鐮佸瓧娈碉紝閬垮厤 shadow 琚敼
++ usermod -L "${DEPLOY_USER}" 2>/dev/null || true
++ ok "鏂板缓鐢ㄦ埛锛?{DEPLOY_USER}锛堝瘑鐮佸凡閿佸畾锛屼粎 SSH key 鐧诲綍锛?
++else
++ info "鐢ㄦ埛 ${DEPLOY_USER} 宸插瓨鍦紝璺宠繃鍒涘缓"
++fi
++
++# -------- 2. 鍔犲叆 docker 缁?--------
++usermod -aG docker "${DEPLOY_USER}"
++ok "${DEPLOY_USER} 鍔犲叆 docker 缁勶紙鏈夋潈闄愭墽琛?docker compose锛?
++
++# -------- 3. 鐢熸垚 SSH 瀵嗛挜锛堟棤瀵嗙爜锛?--------
++SSH_DIR="/home/${DEPLOY_USER}/.ssh"
++mkdir -p "${SSH_DIR}"
++chmod 700 "${SSH_DIR}"
++chown "${DEPLOY_USER}:${DEPLOY_USER}" "${SSH_DIR}"
++
++KEY_PATH="${SSH_DIR}/id_ed25519"
++if [[ ! -f "${KEY_PATH}" ]]; then
++ sudo -u "${DEPLOY_USER}" ssh-keygen -t ed25519 \
++ -C "gitea-actions-deploy-$(hostname)" \
++ -f "${KEY_PATH}" \
++ -N "" >/dev/null
++ ok "SSH ed25519 瀵嗛挜瀵圭敓鎴愶細${KEY_PATH}"
++else
++ info "瀵嗛挜瀵瑰凡瀛樺湪锛岃烦杩囩敓鎴愶細${KEY_PATH}"
++fi
++
++# 鍏挜鍔犲叆 authorized_keys锛堝厑璁告寔瀵瑰簲绉侀挜鐨勪汉浠?deploy 鐢ㄦ埛 ssh 鐧诲綍锛?+AUTH_KEYS="${SSH_DIR}/authorized_keys"
++touch "${AUTH_KEYS}"
++PUBKEY_CONTENT="$(cat "${KEY_PATH}.pub")"
++if ! grep -qF "${PUBKEY_CONTENT}" "${AUTH_KEYS}" 2>/dev/null; then
++ echo "${PUBKEY_CONTENT}" >> "${AUTH_KEYS}"
++ ok "鍏挜宸插啓鍏?authorized_keys"
++fi
++chmod 600 "${AUTH_KEYS}"
++chmod 644 "${KEY_PATH}.pub"
++chmod 600 "${KEY_PATH}"
++chown -R "${DEPLOY_USER}:${DEPLOY_USER}" "${SSH_DIR}"
++
++# -------- 4. sshd 鍔犲浐锛堝彧鍏佽鍏挜鐧诲綍锛涘埆璁?root 瀵嗙爜鐧诲綍锛沝eploy 绂佹杞彂锛?-------
++SSHD_SNIPPET="/etc/ssh/sshd_config.d/99-deploy-user.conf"
++if [[ ! -f "${SSHD_SNIPPET}" ]]; then
++ mkdir -p /etc/ssh/sshd_config.d
++ cat > "${SSHD_SNIPPET}" </dev/null || service ssh reload || true
++ ok "sshd 閰嶇疆宸插姞鍥猴紙deploy 鍙厑璁稿叕閽ョ櫥褰曪紝骞剁鐢ㄧ鍙h浆鍙戯級"
++fi
++
++# -------- 5. 鍒涘缓閮ㄧ讲鐩綍骞剁粰 deploy 鏉冮檺 --------
++mkdir -p "${DEPLOY_DIR}"
++# 鏃㈣缁勬潈闄愶紝鍙堝姞 ACL锛岀‘淇濆悗缁柊寤烘枃浠朵篃鏈?+setfacl -R -m "u:${DEPLOY_USER}:rwX" "${DEPLOY_DIR}"
++setfacl -R -d -m "u:${DEPLOY_USER}:rwX" "${DEPLOY_DIR}"
++ok "閮ㄧ讲鐩綍 ${DEPLOY_DIR} 宸茬粰 ${DEPLOY_USER} 瀹屾暣璇诲啓鏉冮檺锛? 鏂板缓鏂囦欢缁ф壙鏉冮檺锛?
++
++# 濡傛灉鐩綍閲屾湁 compose/鐜鏂囦欢锛岄『渚挎敼涓€涓嬪睘缁勶紝閬垮厤銆宺oot 寤虹殑鏂囦欢 deploy 鏀逛笉浜嗐€?+chgrp -R docker "${DEPLOY_DIR}" 2>/dev/null || true
++chmod -R g+rwX "${DEPLOY_DIR}" 2>/dev/null || true
++
++# -------- 6. 楠岃瘉 docker 鏄惁鐪熻兘鐢紙涓嶆姤閿欙級--------
++DOCKER_RC=0
++sudo -u "${DEPLOY_USER}" docker ps >/dev/null 2>&1 || DOCKER_RC=$?
++if [[ ${DOCKER_RC} -eq 0 ]]; then
++ ok "鉁?${DEPLOY_USER} 鍙互鎵ц docker 鍛戒护"
++else
++ warn "鈿狅笍 ${DEPLOY_USER} 鏆傛椂璺戜笉浜?docker锛堝彲鑳介渶瑕侀噸鏂扮櫥褰曚竴娆″埛鏂扮粍锛夛紝閮ㄧ讲鏃朵笉浼氭湁褰卞搷锛堜絾楠岃瘉鏃朵細鍐嶈Е鍙戜竴娆★級"
++fi
++
++# -------- 7. 鎵撳嵃淇℃伅 --------
++FINGERPRINT="$(ssh-keygen -l -f "${KEY_PATH}.pub" 2>/dev/null || true)"
++PUBKEY_CONTENT="$(cat "${KEY_PATH}.pub")"
++PRIVATE_CONTENT="$(cat "${KEY_PATH}")"
++
++clear
++echo -e "${GREEN}================================================================================${NC}"
++echo -e "${GREEN} 馃帀 閮ㄧ讲鐢ㄦ埛 & SSH 瀵嗛挜 閰嶇疆瀹屾垚锛?{NC}"
++echo -e "${GREEN}================================================================================${NC}"
++echo -e " 馃懁 閮ㄧ讲鐢ㄦ埛鍚? 锛?{CYAN}${DEPLOY_USER}${NC}"
++echo -e " 馃搨 閮ㄧ讲鐩綍 锛?{CYAN}${DEPLOY_DIR}${NC}"
++echo -e " 馃攽 绉侀挜鏂囦欢璺緞 锛?{CYAN}${KEY_PATH}${NC}"
++echo -e " 馃Ь 鍏挜鎸囩汗 锛?{FINGERPRINT}"
++echo -e " 馃Ь 鍏挜鍐呭锛堟柟渚挎牳瀵癸級锛?
++echo -e " ${PUBKEY_CONTENT}"
++echo ""
++echo -e "${YELLOW}================================================================================${NC}"
++echo -e "${YELLOW} 鈿狅笍 涓嬮潰杩欎竴鏁存鏄€愮閽ャ€戯紝瀹屾暣澶嶅埗锛堜粠 BEGIN 鍒?END 鍖呮嫭涓よ锛夊埌锛?{NC}"
++echo -e "${YELLOW} Gitea 浠撳簱 鈫?Settings 鈫?Secrets and variables 鈫?Actions 鈫?New Secret${NC}"
++echo -e "${YELLOW} Name = DEPLOY_SSH_KEY${NC}"
++echo -e "${YELLOW} Value = 绮樿创涓嬮潰涓€鏁存锛堝寘鎷?-----BEGIN/END-----锛?{NC}"
++echo -e "${YELLOW}================================================================================${NC}"
++echo ""
++echo "${PRIVATE_CONTENT}"
++echo ""
++echo -e "${YELLOW}================================================================================${NC}"
++echo -e "${YELLOW} 鈿狅笍 涓婇潰灏辨槸 DEPLOY_SSH_KEY锛屼笉瑕佸湪浠讳綍鍦版柟璐村埌鍒殑鏂囨。鎴栬亰澶┿€傜湅瀹?Ctrl+L 娓呭睆銆?{NC}"
++echo -e "${YELLOW}================================================================================${NC}"
++echo ""
++echo -e "${CYAN}馃憠 鍚屾椂浣犺繕闇€瑕佽ˉ浠ヤ笅鍑犱釜 Repository Secrets锛堥兘鍦ㄥ悓涓€椤垫坊鍔狅級锛?{NC}"
++echo -e " 鈥?DEPLOY_HOST = 浣犵殑鏈嶅姟鍣ㄥ叕缃?IP 鎴栧煙鍚?
++echo -e " 鈥?DEPLOY_PORT = SSH 绔彛锛堜竴鑸?22锛?
++echo -e " 鈥?DEPLOY_USER = ${DEPLOY_USER}锛堝氨鏄繖涓敤鎴峰悕锛?
++echo -e " 鈥?REGISTRY_TOKEN = 浣犵殑 Gitea 涓汉璁块棶浠ょ墝锛圥ackage 璇诲啓鏉冮檺锛?
++echo -e " 鈥?REGISTRY_PULL_USER = 鍚?REGISTRY_USER锛堟湇鍔″櫒涓婃媺闀滃儚鐢紝涓€鑸拰 REGISTRY_USER 涓€鏍凤級"
++echo -e " 鈥?REGISTRY_PULL_TOKEN= 涓婇潰浠ょ墝鍚屼竴涓垨鏂板缓涓€涓粎 pull 鏉冮檺鐨勬洿瀹夊叏"
++echo -e " 鈥?NUXT_PUBLIC_SITE_URL = https://浣犵殑鍩熷悕.com锛堢敤鏉ユ樉绀洪儴缃叉垚鍔熷悗鐨勮烦杞寜閽級"
++echo ""
+diff --git a/scripts/setup-runner.sh b/scripts/setup-runner.sh
+new file mode 100644
+index 0000000..5ee9b05
+--- /dev/null
++++ b/scripts/setup-runner.sh
+@@ -0,0 +1,380 @@
++#!/usr/bin/env bash
++# ============================================================================
++# setup-runner.sh 鈥斺€?涓€閿畨瑁呭苟娉ㄥ唽 Gitea Actions Runner锛坅ct_runner锛?+#
++# 鉁?鍏煎 Gitea 1.21 ~ 鏈€鏂帮紙鍚?1.27.x锛夛紝鏂扮増娌℃湁 Platform 涓嬫媺鑿滃崟銆?+#
++# 浣跨敤鏂规硶锛堜袱绉嶆ā寮忎换閫夊叾涓€锛屾帹鑽?妯″紡 A 鏈€鐪佷簨锛夛細
++#
++# 鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣
++# 妯″紡 A锛氫粨搴撶骇 Runner锛堝彧缁欒繖涓?VoiceHub 浠撳簱鐢紝鏈€甯哥敤銆佹潈闄愭渶灏忥級
++# 璺緞锛氫綘鐨勪粨搴?鈫?Settings 鈫?Actions 鈫?Runners 鈫?鍙充笂瑙?+# 鈫?鏂扮増锛?.21+锛夎繖閲屾病鏈?Platform 閫夋嫨锛岀洿鎺ユ湁涓€涓緭鍏ユ
++# 鍐欑潃銆孯egistration token銆嶆垨銆屾敞鍐屼护鐗屻€?+# 鈫?鐐瑰彸杈圭殑 馃搵 澶嶅埗鎸夐挳锛屽緱鍒颁竴涓蹭互 R 寮€澶寸殑 token
++# 渚嬶細 RGT8FZxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxV
++#
++# 鏈哄櫒涓婃墽琛岋細
++# sudo bash scripts/setup-runner.sh \
++# --scope repo --gitea https://浣犵殑-gitea --token RGTxxxx...
++#
++# 鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣
++# 妯″紡 B锛氱粍缁囩骇 Runner锛堝悓涓€涓?Gitea 缁勭粐涓嬫墍鏈変粨搴撻兘鑳界敤锛?+# 璺緞锛氱粍缁囦富椤?鈫?Settings 鈫?Actions 鈫?Runners 鈫?澶嶅埗 Registration token
++# 锛圱oken 寮€澶翠竴鑸槸 RGOxxxx锛?+#
++# 鎵ц锛?+# sudo bash scripts/setup-runner.sh \
++# --scope org --gitea https://浣犵殑-gitea --token RGOxxxx...
++#
++# 鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣
++# 妯″紡 C锛氬叏灞€/绔欑偣绾?Runner锛圙itea 鍏ㄧ珯鎵€鏈変粨搴撻兘鑳界敤锛岄渶绔欑偣绠$悊鍛橈級
++# 璺緞锛氱珯鐐圭鐞嗗悗鍙帮紙Site Administration锛夆啋 Actions 鈫?Runners
++# 鈫?澶嶅埗 Registration token锛堝紑澶翠竴鑸槸 RGxxxx锛?+#
++# 鎵ц锛?+# sudo bash scripts/setup-runner.sh \
++# --scope global --gitea https://浣犵殑-gitea --token RGxxxx...
++#
++# 鍏朵粬鍙€夊弬鏁帮細
++# --name "my-runner-01" 鑷畾涔?Runner 鍚嶅瓧锛堥粯璁ゆ槸 runner-涓绘満鍚嶏級
++# --user gitrunner 杩愯鐢ㄦ埛锛堥粯璁?gitrunner锛?+# --dir /opt/act_runner 瀹夎鐩綍锛堥粯璁?/opt/act_runner锛?+# --version 3.3.2 銆愬彲閫夈€戝浐瀹氬畨瑁呮煇鐗堟湰锛屼笉鍐欓粯璁ょ敤 Gitea 瀹樻柟 latest
++# ============================================================================
++set -euo pipefail
++
++# ---------- 棰滆壊 ----------
++RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[1;36m'; NC='\033[0m'
++info() { echo -e "${GREEN}[INFO]${NC} $*"; }
++warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
++err() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
++ok() { echo -e "${CYAN}[ OK ]${NC} $*"; }
++
++# ---------- 瑙f瀽鍙傛暟 ----------
++GITEA_URL=""
++REG_TOKEN=""
++SCOPE="repo" # repo | org | global锛堥粯璁?repo锛岀粷澶у鏁颁汉鐨勫満鏅級
++RUNNER_NAME="runner-$(hostname | tr '[:upper:]' '[:lower:]')"
++RUNNER_USER="gitrunner"
++INSTALL_DIR="/opt/act_runner"
++PINNED_VERSION="" # 绌?= latest锛屽惁鍒欐瘮濡?"3.3.2" 鍥哄畾鐗堟湰
++while [[ $# -gt 0 ]]; do
++ case "$1" in
++ --gitea) GITEA_URL="${2}"; shift 2 ;;
++ --token) REG_TOKEN="${2}"; shift 2 ;;
++ --scope) SCOPE="${2}"; shift 2 ;;
++ --name) RUNNER_NAME="${2}"; shift 2 ;;
++ --user) RUNNER_USER="${2}"; shift 2 ;;
++ --dir) INSTALL_DIR="${2}"; shift 2 ;;
++ --version) PINNED_VERSION="${2#v}"; shift 2 ;;
++ -h|--help)
++ sed -n '2,60p' "$0" | sed 's/^# \{0,1\}//'
++ exit 0
++ ;;
++ *) err "鏈煡鍙傛暟锛?1锛?-help 鏌ョ湅甯姪锛? ;;
++ esac
++done
++
++[[ -n "${GITEA_URL}" ]] || err "蹇呴』浼?--gitea https://浣犵殑-gitea-鍦板潃"
++[[ -n "${REG_TOKEN}" ]] || err "蹇呴』浼?--token锛堜粠 Gitea 椤甸潰鐨?Registration token 澶嶅埗锛孯xx 寮€澶寸殑涓€涓诧級"
++case "${SCOPE}" in
++ repo|org|global) ;;
++ *) err "--scope 蹇呴』鏄?repo | org | global 鍏朵腑涔嬩竴" ;;
++esac
++[[ $EUID -eq 0 ]] || err "璇风敤 sudo 杩愯鏈剼鏈紙闇€瑕佸啓 /opt / /etc/systemd/system锛?
++
++# 鍘绘帀鏈熬 /
++GITEA_URL="${GITEA_URL%/}"
++
++echo ""
++echo -e "${CYAN}鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣${NC}"
++echo -e "${CYAN} 馃殌 寮€濮嬮儴缃?Gitea Actions Runner (鍏煎 1.21 ~ 1.27+)${NC}"
++echo -e "${CYAN} 鈥?缁戝畾 Gitea : ${GITEA_URL}${NC}"
++echo -e "${CYAN} 鈥?浣滅敤鍩? : ${SCOPE}${NC}"
++echo -e "${CYAN} 鈥?Runner 鍚嶇О : ${RUNNER_NAME}${NC}"
++echo -e "${CYAN}鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣${NC}"
++echo ""
++
++# ---------- 0. 鏋舵瀯妫€娴?----------
++ARCH="$(uname -m)"
++case "${ARCH}" in
++ x86_64|amd64) PLATFORM="linux-amd64" ;;
++ aarch64|arm64) PLATFORM="linux-arm64" ;;
++ armv7l) PLATFORM="linux-arm" ;;
++ *) err "鏆備笉鏀寔鐨勬灦鏋勶細${ARCH}锛堥渶瑕?x86_64 / aarch64 / armv7锛? ;;
++esac
++ok "妫€娴嬪埌鏋舵瀯锛?{ARCH} 鈫?涓嬭浇鐗堟湰锛?{PLATFORM}"
++
++# ---------- 0.5 缁欐柊鐗?Gitea 鐢ㄦ埛鐨?Token 鏍煎紡鎻愮ず ----------
++# 涓嶅悓鐗堟湰 / 璇█鍖呯殑 Token 鍓嶇紑涓嶄竴鏍凤紝鍏煎 4 绉嶇湡瀹炲瓨鍦ㄧ殑鏍煎紡锛?+# 鈥?鑻辨枃 1.21+ 浠撳簱绾?RGTxxxx / 缁勭粐绾?RGOxxxx / 鍏ㄥ眬 RGxxxx
++# 鈥?涓枃璇█鍖?/ 鏌愪簺浜屾鍒嗗彂鐗堟湰锛氱函瀛楁瘝鏁板瓧 20~64 浣嶏紙濡?LHLDAwsho7vVSukoEeKhft锛?+TOKEN_LEN="${#REG_TOKEN}"
++ok "Token 闀垮害 = ${TOKEN_LEN} 浣?
++case "${REG_TOKEN}" in
++ # 鑻辨枃鍘熺増鍓嶇紑
++ RG*) info "鈥?Token 鍓嶇紑 RG锛氬叏灞€/绔欑偣绾э紙鑻辨枃鍘熺増锛夆渽" ;;
++ RGO*) info "鈥?Token 鍓嶇紑 RGO锛氱粍缁囩骇锛堣嫳鏂囧師鐗堬級鉁? ;;
++ RGT*) info "鈥?Token 鍓嶇紑 RGT锛氫粨搴撶骇锛堣嫳鏂囧師鐗堬級鉁? ;;
++ # 绾瓧姣嶆暟瀛楋紙涓枃 UI / 浜屽紑鐗堟湰甯歌锛?+ [A-Za-z0-9]\{16,128\})
++ ok "鈥?绾瓧姣嶆暟瀛?Token锛堜腑鏂?UI / 鏌愪簺浜屽紑鐗堟湰甯歌锛夆渽锛屽拰鎴浘閲岀殑銆屾敞鍐屼护鐗屻€嶄竴鑷?
++ ;;
++ # 鍏朵粬
++ *)
++ warn "鈥?Token 鐪嬭捣鏉ヤ笉鏄?Gitea 甯歌鏍煎紡銆備笉杩囦篃鍙兘鏄柊鐗堟敼鍔紝缁х画灏濊瘯娉ㄥ唽..."
++ warn " 濡傛灉娉ㄥ唽鎶?401/403锛屽缓璁洖鍒伴〉闈㈢偣涓€娆°€岄噸缃敞鍐屼护鐗屻€嶉噸鏂板鍒躲€?
++ ;;
++esac
++# 鍩烘湰瀛楃闆嗘牎楠岋紙闃叉澶嶅埗鏃剁矘浜嗙┖鏍兼垨鎹㈣锛?+if [[ ! "${REG_TOKEN}" =~ ^[A-Za-z0-9]+$ ]]; then
++ warn "鈿狅笍 Token 閲屽惈鏈夐潪瀛楁瘝鏁板瓧鐨勫瓧绗︼紝寰堝彲鑳芥槸澶嶅埗鏃跺甯︿簡绌烘牸/鎹㈣/鏍囩偣锛?
++ warn " 寤鸿鍥炲埌浠撳簱椤甸潰鐐?馃搵 澶嶅埗鎸夐挳锛堜笉瑕佹墜閫夋枃瀛楋級銆?
++fi
++
++# ---------- 1. 瀹夎渚濊禆锛歞ocker锛圧unner 瑕佺敤 Docker-in-Docker 鎵ц buildx 绛夛級----------
++if ! command -v docker >/dev/null 2>&1; then
++ info "妫€娴嬪埌娌¤ Docker锛岀幇鍦ㄧ敤瀹樻柟涓€閿剼鏈畨瑁咃紙鍥藉唴鑷姩鎹㈡竻鍗庢簮锛?.."
++ curl -fsSL https://get.docker.com -o /tmp/get-docker.sh || err "涓嬭浇 docker-install 鑴氭湰澶辫触"
++ if curl -sI https://mirrors.tuna.tsinghua.edu.cn >/dev/null; then
++ bash /tmp/get-docker.sh --mirror Aliyun || bash /tmp/get-docker.sh
++ else
++ bash /tmp/get-docker.sh
++ fi
++ systemctl enable --now docker
++ info "鉁?Docker 瀹夎瀹屾垚锛?(docker --version)"
++else
++ info "鉁?Docker 宸插瓨鍦細$(docker --version)"
++fi
++
++# ---------- 2. 寤鸿繍琛岀敤鎴?gitrunner锛屽姞鍏?docker 缁?----------
++if ! id -u "${RUNNER_USER}" >/dev/null 2>&1; then
++ useradd -r -s /usr/sbin/nologin -d "${INSTALL_DIR}" "${RUNNER_USER}"
++ info "鉁?鏂板缓绯荤粺鐢ㄦ埛 ${RUNNER_USER}"
++else
++ info "鈩癸笍 鐢ㄦ埛 ${RUNNER_USER} 宸插瓨鍦紝璺宠繃鍒涘缓"
++fi
++usermod -aG docker "${RUNNER_USER}"
++info "鉁?鎶?${RUNNER_USER} 鍔犲叆 docker 缁勶紙鍏佽鎿嶄綔 Docker daemon锛?
++
++mkdir -p "${INSTALL_DIR}"
++chown -R "${RUNNER_USER}:${RUNNER_USER}" "${INSTALL_DIR}"
++
++# ---------- 3. 涓嬭浇鏈€鏂?Gitea Runner锛坅ct_runner 0.x 鈫?3.x 鏀瑰悕 gitea-runner锛?+# 娉ㄦ剰锛氬畼鏂逛粠 v3 璧凤紝浠撳簱浠?gitea/act_runner 鏀瑰埌浜?gitea/runner锛屼笖鏂囦欢鍚嶅墠缂€浠?act_runner-* 鈫?gitea-runner-*銆?+# 涓洪伩鍏嶅悗缁増鏈啀鏀瑰懡鍚嶅鑷?404锛岃繖閲?*鐩存帴鐢?API 杩斿洖鐨?assets[].browser_download_url**锛屼笉鍐嶇‖缂栫爜鎷兼枃浠跺悕銆?+# ----------
++# 3.1 鍐冲畾 release URL锛堝浐瀹氱増鏈?or latest锛?+if [[ -n "${PINNED_VERSION}" ]]; then
++ REL_URL="https://gitea.com/api/v1/repos/gitea/runner/releases/tags/v${PINNED_VERSION}"
++ info "猬囷笍 浣犳寚瀹氫簡鍥哄畾鐗堟湰锛歷${PINNED_VERSION}"
++else
++ REL_URL="https://gitea.com/api/v1/repos/gitea/runner/releases/latest"
++ info "猬囷笍 姝e湪鑾峰彇 Gitea Runner 鏈€鏂?release 鍏冩暟鎹?.."
++fi
++LATEST_JSON="$(curl -fsSL "${REL_URL}" 2>/dev/null || true)"
++
++# 3.2 濡傛灉 gitea.com 涓嶉€氾紙绾唴缃戯級锛岀洿鎺ュ洖閫€鍒颁竴涓垜浠凡鐭ュ彲鐢ㄧ殑鐗堟湰
++if [[ -z "${LATEST_JSON}" ]]; then
++ if [[ -z "${PINNED_VERSION}" ]]; then
++ warn "gitea.com 璁块棶涓嶅埌锛堝彲鑳芥槸鍐呯綉鐜锛夛紝鑷姩鍥為€€鍒板凡鐭ョǔ瀹氱増鏈細v3.3.2"
++ PINNED_VERSION="3.3.2"
++ fi
++ TAG="v${PINNED_VERSION}"
++ FALLBACK_MODE=1
++else
++ TAG="$(echo "${LATEST_JSON}" | grep -oE '"tag_name":\s*"[^"]+"' | head -1 | cut -d'"' -f4)"
++ FALLBACK_MODE=0
++fi
++[[ -n "${TAG}" ]] || TAG="v3.3.2"
++VERSION="${TAG#v}"
++
++# 3.3 浼樺厛浠?assets 鍖归厤姝g‘ platform 鐨?browser_download_url
++BIN_URL=""
++ASSET_NAME=""
++if [[ "${FALLBACK_MODE}" -eq 0 && -n "${LATEST_JSON}" ]]; then
++ # 鐢?awk 杩囨护锛?+ # - 鍖归厤 PLATFORM锛堜緥濡?linux-amd64锛?+ # - 鎺掗櫎 .xz / .sha256 / .exe 绛夋牎楠屽拰/鍘嬬缉鍖?+ # - 涓ょ鍓嶇紑閮藉吋瀹癸細gitea-runner-X.X.X- (鏂扮増) 鎴?act_runner-X.X.X- (鏃х増)
++ while IFS='|' read -r name url; do
++ [[ -z "${name}" ]] && continue
++ case "${name}" in
++ *"${PLATFORM}"* | *"${PLATFORM//-/_}"*) ;;
++ *) continue ;;
++ esac
++ case "${name}" in
++ *.xz|*.sha256|*.exe|*.tar.gz|*.zip|*checksums*) continue ;;
++ esac
++ ASSET_NAME="${name}"
++ BIN_URL="${url}"
++ break
++ done < <(echo "${LATEST_JSON}" | awk '
++ /"assets": *\[/ {inA=1; next}
++ inA && /"browser_download_url"/ { gsub(/[",]/,"",$2); url=$2 }
++ inA && /"name"/ {
++ gsub(/[",]/,"",$2)
++ if (url!="") { print $2 "|" url; url="" }
++ }
++ inA && /^\]/ {inA=0}
++ ')
++fi
++
++# 3.4 濡傛灉 assets 鍖归厤澶辫触锛堣€佺増鏈?JSON 鏍煎紡鐗规畩锛夛紝鎵嬪姩鎷?4 涓€欓€?URL 椤哄簭灏濊瘯
++if [[ -z "${BIN_URL}" ]]; then
++ info "assets 鑷姩鍖归厤涓虹┖锛屾敼鐢ㄦ墜鍔ㄥ€欓€夊垪琛紙鍏煎鏂版棫鍛藉悕锛?.."
++ CANDIDATES=(
++ # 鏂扮増锛坴3+锛塯itea-runner
++ "https://gitea.com/gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-${PLATFORM}"
++ # github 闀滃儚锛堟湁鏃?gitea.com 鎱㈡垨鎸傦級
++ "https://github.com/go-gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-${PLATFORM}"
++ # 鏃х増 act_runner锛坴0.2.x 閬楃暀鍛藉悕锛?+ "https://dl.gitea.com/act_runner/${VERSION}/act_runner-${VERSION}-${PLATFORM}"
++ # 鏃х増 gitea.com
++ "https://gitea.com/gitea/act_runner/releases/download/v${VERSION}/act_runner-${VERSION}-${PLATFORM}"
++ )
++ for C in "${CANDIDATES[@]}"; do
++ info " 鈫?灏濊瘯锛?{C}"
++ if curl -fsSI "${C}" >/dev/null 2>&1; then
++ BIN_URL="${C}"
++ ASSET_NAME="$(basename "${C}")"
++ break
++ fi
++ done
++fi
++
++# 3.5 杩樻病鎷垮埌 URL 鈫?鎵撳嵃鎵嬪姩涓嬭浇鎸囧紩锛堜竴瀹氳鐢ㄦ埛鑳芥晳鍥炴潵锛?+if [[ -z "${BIN_URL}" ]]; then
++ {
++ echo ""
++ echo -e "${RED}鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣${NC}"
++ echo -e "${RED} 鉂?鑷姩涓嬭浇 Gitea Runner 澶辫触${NC}"
++ echo -e "${RED}鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣鈹佲攣${NC}"
++ echo " 浣犵殑鏋舵瀯锛?{PLATFORM} 锛?鐩爣鐗堟湰锛?{TAG}"
++ echo ""
++ echo " 璇峰湪鑳藉嚭缃戠殑鏈哄櫒涓婏紝鎵嬪姩鎵撳紑涓嬮潰 2 涓?URL 涔嬩竴锛屼笅杞藉搴旂殑浜岃繘鍒讹細"
++ echo " 鈥?https://gitea.com/gitea/runner/releases/tag/${TAG}"
++ echo " 鈥?https://github.com/go-gitea/runner/releases/tag/${TAG}"
++ echo " 鎵炬枃浠跺悕绫讳技锛歡itea-runner-${VERSION}-${PLATFORM}锛堜笉瑕佷笅 .xz / .sha256锛?
++ echo ""
++ echo " 涓嬭浇瀹屾垚鍚?scp 鍒版湰鏈嶅姟鍣ㄥ苟鎵ц锛?
++ echo " sudo cp ./gitea-runner-* /usr/local/bin/act_runner"
++ echo " sudo chmod +x /usr/local/bin/act_runner"
++ echo " # 鐒跺悗閲嶆柊璺戞湰鑴氭湰锛堝凡鏈夌殑鐢ㄦ埛/缁?config 涓嶄細閲嶅鐢熸垚锛?
++ echo ""
++ } >&2
++ exit 1
++fi
++
++# 3.6 鐪熸涓嬭浇
++info "猬囷笍 涓嬭浇 Gitea Runner ${TAG}锛?{ASSET_NAME}"
++info " URL锛?{BIN_URL}"
++curl -fsSL "${BIN_URL}" -o /usr/local/bin/act_runner \
++ || err "涓嬭浇澶辫触锛佸彲鑳芥槸 DNS/缃戠粶/璇佷功闂銆傚彲浠ユ墜鍔ㄦ妸涓婇潰 URL 鐨勬枃浠朵笅杞戒笅鏉?scp 鍒版湇鍔″櫒鏀惧埌 /usr/local/bin/act_runner锛屽啀閲嶆柊璺戞湰鑴氭湰銆?
++chmod +x /usr/local/bin/act_runner
++INSTALLED_VER="$(act_runner --version 2>&1 || echo "${TAG}")"
++ok "鉁?宸插畨瑁呭埌 /usr/local/bin/act_runner锛岀増鏈細${INSTALLED_VER}"
++
++# ---------- 4. 娉ㄥ唽 Runner ----------
++cd "${INSTALL_DIR}"
++info "馃敆 寮€濮嬫敞鍐?Runner 鍒?${GITEA_URL}锛堝悕绉帮細${RUNNER_NAME}锛?
++
++# 鍒囧埌 gitrunner 鐢ㄦ埛娉ㄥ唽锛岄伩鍏?root 璺戯紙瀹夊叏锛?+rm -f .runner 2>/dev/null || true
++sudo -u "${RUNNER_USER}" env HOME="${INSTALL_DIR}" \
++ act_runner register \
++ --instance "${GITEA_URL}" \
++ --token "${REG_TOKEN}" \
++ --name "${RUNNER_NAME}" \
++ --labels "ubuntu-latest:docker://catthehacker/ubuntu:act-22.04" \
++ --no-interactive \
++ || err "娉ㄥ唽澶辫触锛佸父瑙佸師鍥狅細\n 1) token 涓嶅锛堝鍒舵椂鍒甫绌烘牸锛塡n 2) Runner 鏈哄櫒鍑轰笉浜嗙綉鍒?${GITEA_URL}\n 3) Gitea 绔欑偣鏃堕棿鍜岃繖鍙版満鍣ㄥ樊澶繙锛堣瘉涔﹂獙璇佸け璐ワ級"
++
++chown "${RUNNER_USER}:${RUNNER_USER}" .runner 2>/dev/null || true
++
++# ---------- 5. 鐢熸垚閰嶇疆鏂囦欢锛堝缓璁紑鍚?docker.host 鐗规潈锛?---------
++CONFIG_FILE="${INSTALL_DIR}/config.yaml"
++if [[ ! -f "${CONFIG_FILE}" ]]; then
++ cat > "${CONFIG_FILE}" <<'YAML'
++log:
++ level: info
++runner:
++ file: .runner
++ capacity: 2 # 鍚屼竴鍙?Runner 鏈€澶氬悓鏃惰窇 2 涓?Job
++ envs:
++ GOPROXY: https://goproxy.cn,direct
++ NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
++ timeout: 3h
++cache:
++ enabled: true
++ dir: /opt/act_runner/cache
++container:
++ network: "bridge"
++ privileged: true # build-push-action 鐢ㄥ埌 Docker daemon
++ options: |
++ --add-host "host.docker.internal:host-gateway"
++YAML
++ chown "${RUNNER_USER}:${RUNNER_USER}" "${CONFIG_FILE}"
++ mkdir -p "${INSTALL_DIR}/cache"
++ chown -R "${RUNNER_USER}:${RUNNER_USER}" "${INSTALL_DIR}/cache"
++ info "鉁?榛樿 config 鍐欏叆 ${CONFIG_FILE}"
++fi
++
++# ---------- 6. 鍐?systemd 鏈嶅姟锛屽紑鏈鸿嚜鍚?----------
++UNIT_FILE="/etc/systemd/system/gitea-act-runner.service"
++cat > "${UNIT_FILE}" < {
+ })
+ }
+
+- if (!['SONG', 'KEYWORD'].includes(type)) {
++ if (!['SONG', 'KEYWORD', 'LANGUAGE', 'GENRE'].includes(type)) {
+ throw createError({
+ statusCode: 400,
+ message: '鏃犳晥鐨勯粦鍚嶅崟绫诲瀷'
+diff --git a/server/api/admin/roles/index.get.ts b/server/api/admin/roles/index.get.ts
+index b11097b..f7d2c68 100644
+--- a/server/api/admin/roles/index.get.ts
++++ b/server/api/admin/roles/index.get.ts
+@@ -1,4 +1,4 @@
+-import { ROLES } from '../../../utils/permissions.js'
++import { ROLES } from '~~/server/utils/permissions'
+
+ export default defineEventHandler((event) => {
+ const user = event.context.user
+diff --git a/server/api/admin/roles/index.put.ts b/server/api/admin/roles/index.put.ts
+index b3b4920..c2f8007 100644
+--- a/server/api/admin/roles/index.put.ts
++++ b/server/api/admin/roles/index.put.ts
+@@ -1,4 +1,4 @@
+-import { ROLES, saveRolesConfig } from '../../../utils/permissions.js'
++import { ROLES, saveRolesConfig } from '~~/server/utils/permissions'
+
+ export default defineEventHandler(async (event) => {
+ const user = event.context.user
+diff --git a/server/api/admin/schedule.post.ts b/server/api/admin/schedule.post.ts
+index 16d0e40..44f6905 100644
+--- a/server/api/admin/schedule.post.ts
++++ b/server/api/admin/schedule.post.ts
+@@ -1,5 +1,13 @@
+ import { db } from '~/drizzle/db'
+-import { playTimes, schedules, songs, users, votes, songReplayRequests } from '~/drizzle/schema'
++import {
++ playTimes,
++ schedules,
++ songs,
++ users,
++ votes,
++ songReplayRequests,
++ scheduleSongPool
++} from '~/drizzle/schema'
+ import { and, asc, count, desc, eq, gte, lte } from 'drizzle-orm'
+ import { createSongSelectedNotification } from '../../services/notificationService'
+ import { cacheService } from '~~/server/services/cacheService'
+@@ -126,6 +134,19 @@ export default defineEventHandler(async (event) => {
+ // 鑾峰彇瀹㈡埛绔疘P鍦板潃
+ const clientIP = getClientIP(event)
+
++ await tx
++ .update(scheduleSongPool)
++ .set({
++ status: 'ASSIGNED',
++ targetPlayDate: playDate
++ })
++ .where(
++ and(
++ eq(scheduleSongPool.songId, body.songId),
++ eq(scheduleSongPool.status, 'PENDING')
++ )
++ )
++
+ // 鍙湁鍦ㄩ潪鑽夌妯″紡涓嬫墠鍒涘缓閫氱煡
+ if (!isDraft) {
+ await createSongSelectedNotification(
+diff --git a/server/api/admin/schedule/draft-bulk.post.ts b/server/api/admin/schedule/draft-bulk.post.ts
+index fb63559..980fa22 100644
+--- a/server/api/admin/schedule/draft-bulk.post.ts
++++ b/server/api/admin/schedule/draft-bulk.post.ts
+@@ -1,5 +1,5 @@
+ import { db } from '~/drizzle/db'
+-import { schedules, songReplayRequests } from '~/drizzle/schema'
++import { schedules, songReplayRequests, scheduleSongPool } from '~/drizzle/schema'
+ import { and, eq, gte, inArray, lte } from 'drizzle-orm'
+ import { cacheService } from '~~/server/services/cacheService'
+
+@@ -80,6 +80,21 @@ export default defineEventHandler(async (event) => {
+
+ const inserted = await tx.insert(schedules).values(rows).returning({ id: schedules.id })
+
++ if (songIds.length > 0) {
++ await tx
++ .update(scheduleSongPool)
++ .set({
++ status: 'ASSIGNED',
++ targetPlayDate: startOfDay
++ })
++ .where(
++ and(
++ inArray(scheduleSongPool.songId, songIds),
++ eq(scheduleSongPool.status, 'PENDING')
++ )
++ )
++ }
++
+ return { deletedCount: existingSchedules.length, insertedCount: inserted.length }
+ })
+
+diff --git a/server/api/admin/songs/batch-reject.post.ts b/server/api/admin/songs/batch-reject.post.ts
+new file mode 100644
+index 0000000..2f0af7a
+--- /dev/null
++++ b/server/api/admin/songs/batch-reject.post.ts
+@@ -0,0 +1,135 @@
++import { db } from '~/drizzle/db'
++import { schedules, songBlacklists, songs, votes, requestTimes } from '~/drizzle/schema'
++import { and, eq, inArray, sql } from 'drizzle-orm'
++import { cacheService } from '../../../services/cacheService'
++import { createSongRejectedNotification } from '../../../services/notificationService'
++import { requireSongAdmin } from '~~/server/utils/requireSongAdmin'
++import { createApiError } from '~~/server/utils/apiError'
++import { SERVER_ERROR_CODES } from '~~/server/config/constants'
++
++const MAX_BATCH_REJECT_COUNT = 1000
++
++export default defineEventHandler(async (event) => {
++ const user = requireSongAdmin(event)
++
++ const body = await readBody(event)
++
++ const songIds = Array.isArray(body?.songIds)
++ ? [...new Set(body.songIds.filter((id: any) => Number.isInteger(id) && id > 0))]
++ : []
++ const reason = typeof body?.reason === 'string' ? body.reason.trim() : ''
++ const addToBlacklist = body?.addToBlacklist === true
++
++ if (songIds.length === 0) {
++ throw createApiError(400, SERVER_ERROR_CODES.SONG_BATCH_REJECT_IDS_REQUIRED, '姝屾洸ID鍒楄〃涓嶈兘涓虹┖')
++ }
++
++ if (songIds.length > MAX_BATCH_REJECT_COUNT) {
++ throw createApiError(
++ 400,
++ SERVER_ERROR_CODES.SONG_BATCH_REJECT_LIMIT_EXCEEDED,
++ `鍗曟鏈€澶氶┏鍥?${MAX_BATCH_REJECT_COUNT} 棣栨瓕鏇瞏,
++ { params: [MAX_BATCH_REJECT_COUNT] }
++ )
++ }
++
++ if (!reason) {
++ throw createApiError(400, SERVER_ERROR_CODES.SONG_BATCH_REJECT_REASON_REQUIRED, '椹冲洖鍘熷洜涓嶈兘涓虹┖')
++ }
++
++ try {
++ let rejectedSongs: any[] = []
++
++ await db.transaction(async (tx) => {
++ const existingSongs = await tx.select().from(songs).where(inArray(songs.id, songIds))
++
++ if (existingSongs.length === 0) {
++ throw createApiError(404, SERVER_ERROR_CODES.SONG_NOT_FOUND, '姝屾洸涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎')
++ }
++
++ const existingIds = existingSongs.map((song) => song.id)
++ console.log(`寮€濮嬫壒閲忛┏鍥炴瓕鏇诧紝鍏?${existingIds.length} 棣朻)
++
++ if (addToBlacklist) {
++ const blacklistValues = [...new Set(existingSongs.map((song: any) => `${song.title} - ${song.artist}`))]
++ const existingBlacklist = await tx
++ .select({ value: songBlacklists.value })
++ .from(songBlacklists)
++ .where(and(eq(songBlacklists.type, 'SONG'), inArray(songBlacklists.value, blacklistValues)))
++ const existingSet = new Set(existingBlacklist.map((item) => item.value))
++ const valuesToInsert = blacklistValues.filter((value) => !existingSet.has(value))
++ for (const value of valuesToInsert) {
++ try {
++ await tx.insert(songBlacklists).values({
++ type: 'SONG',
++ value,
++ reason: `姝屾洸椹冲洖: ${reason}`,
++ createdBy: user.id
++ })
++ } catch (error: any) {
++ console.log(`榛戝悕鍗曟坊鍔犲け璐ワ紝鍙兘宸插瓨鍦? ${error.message}`)
++ }
++ }
++ console.log(`榛戝悕鍗曟坊鍔犲鐞嗗畬鎴恅)
++ }
++
++ await tx.delete(votes).where(inArray(votes.songId, existingIds))
++ console.log(`鍒犻櫎浜嗘姇绁ㄨ褰昤)
++
++ await tx.delete(schedules).where(inArray(schedules.songId, existingIds))
++ console.log(`鍒犻櫎浜嗘帓鏈熻褰昤)
++
++ const hitRequestIds = [...new Set(existingSongs.map((song: any) => song.hitRequestId).filter(Boolean))]
++ for (const hitRequestId of hitRequestIds) {
++ const count = existingSongs.filter((song: any) => song.hitRequestId === hitRequestId).length
++ try {
++ await tx
++ .update(requestTimes)
++ .set({
++ accepted: sql`GREATEST(0, accepted - ${count})`
++ })
++ .where(eq(requestTimes.id, hitRequestId as any))
++ } catch (error: any) {
++ console.error(`鍑忓皯鎶曠鏃舵 ${hitRequestId} 鎺ョ撼鏁伴噺澶辫触: ${error.message}`)
++ }
++ }
++
++ rejectedSongs = await tx.delete(songs).where(inArray(songs.id, existingIds)).returning()
++ console.log(`姝屾洸鎵归噺鍒犻櫎瀹屾垚`)
++ })
++
++ const songsToNotify = rejectedSongs.filter((song) => song.requesterId)
++ for (const song of songsToNotify) {
++ createSongRejectedNotification(
++ song.requesterId,
++ { title: song.title, artist: song.artist },
++ reason
++ ).catch((error: any) => {
++ console.error(`鍙戦€侀┏鍥為€氱煡澶辫触: ${error.message}`)
++ })
++ }
++
++ await cacheService.clearSongsCache()
++ await cacheService.clearSchedulesCache()
++
++ console.log(`鎵归噺椹冲洖鎿嶄綔瀹屾垚锛屽叡 ${rejectedSongs.length} 棣朻)
++
++ return {
++ success: true,
++ message: '姝屾洸鎵归噺椹冲洖鎴愬姛',
++ data: {
++ rejected: rejectedSongs.length,
++ missing: songIds.length - rejectedSongs.length,
++ notificationSent: songsToNotify.length
++ }
++ }
++ } catch (error: any) {
++ console.error('鎵归噺椹冲洖姝屾洸澶辫触:', error)
++
++ if (error.statusCode) {
++ throw error
++ }
++
++ throw createApiError(500, SERVER_ERROR_CODES.SONG_OPERATION_FAILED, '鎵归噺椹冲洖澶辫触: ' + error.message)
++ }
++})
+diff --git a/server/api/admin/stats/active-users.get.ts b/server/api/admin/stats/active-users.get.ts
+index ac05201..e5fb760 100644
+--- a/server/api/admin/stats/active-users.get.ts
++++ b/server/api/admin/stats/active-users.get.ts
+@@ -1,10 +1,10 @@
+ import { createError, defineEventHandler, getQuery } from 'h3'
+ import { db } from '~/drizzle/db'
+-import { songs, users, votes } from '~/drizzle/schema'
+-import { CacheService } from '../../../services/cacheService'
++import { songs, users, votes, userIdentities } from '~/drizzle/schema'
++import { CacheService } from '~~/server/services/cacheService'
++import { getUserAvatarUrl } from '~~/server/utils/user-avatar'
+
+ export default defineEventHandler(async (event) => {
+- // 妫€鏌ヨ璇佸拰鏉冮檺
+ const user = event.context.user
+ if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
+ throw createError({
+@@ -18,7 +18,6 @@ export default defineEventHandler(async (event) => {
+ const limit = parseInt(query.limit as string) || 20
+
+ try {
+- // 灏濊瘯浠庣紦瀛樿幏鍙栨暟鎹? const cacheService = CacheService.getInstance()
+ const cachedStats = await cacheService.getActiveUsersStats(semester, limit)
+ if (cachedStats) {
+@@ -26,18 +25,24 @@ export default defineEventHandler(async (event) => {
+ return cachedStats
+ }
+
+- // 鏋勫缓鏌ヨ鏉′欢 - 濡傛灉娌℃湁鎸囧畾瀛︽湡鎴栧鏈熶负'all'锛屾煡璇㈡墍鏈夌敤鎴? let songWhere = {}
+ if (semester && semester !== 'all') {
+ songWhere = { semester: semester }
+ }
+
+- // 鑾峰彇娲昏穬鐢ㄦ埛鏁版嵁 - 鏈夋姇绋挎瓕鏇茬殑鐢ㄦ埛
+ const allUsers = await db.select().from(users)
+ const allSongs = await db.select().from(songs)
+ const allVotes = await db.select().from(votes)
++ const allIdentities = await db.select().from(userIdentities)
++
++ const identitiesMap = new Map()
++ allIdentities.forEach((identity) => {
++ if (!identitiesMap.has(identity.userId)) {
++ identitiesMap.set(identity.userId, [])
++ }
++ identitiesMap.get(identity.userId)!.push(identity)
++ })
+
+- // 杩囨护鏈夋姇绋挎瓕鏇茬殑鐢ㄦ埛
+ const activeUsers = allUsers
+ .filter((user) => {
+ const userSongs = allSongs.filter((song) => {
+@@ -65,44 +70,48 @@ export default defineEventHandler(async (event) => {
+ }
+ })
+
+- // 濡傛灉娌℃湁娲昏穬鐢ㄦ埛锛岃繑鍥炵┖鏁扮粍
+ if (activeUsers.length === 0) {
+ const emptyResult = []
+- // 缂撳瓨绌虹粨鏋? await cacheService.setActiveUsersStats(semester, limit, emptyResult)
+ console.log('[Cache] 娲昏穬鐢ㄦ埛缁熻鏁版嵁宸茬紦瀛橈紙绌虹粨鏋滐級')
+ return emptyResult
+ }
+
+- // 璁$畻姣忎釜鐢ㄦ埛鐨勬椿璺冨害鏁版嵁
+ const userStats = activeUsers.map((user) => {
+ const contributions = user.songs.length
+ const likes = user.songs.reduce((total, song) => total + song.votes.length, 0)
+-
+- // 娲昏穬搴﹁绠楋細鎶曠鏁?* 2 + 鐐硅禐鏁?* 0.5
+ const activityScore = Math.round(contributions * 2 + likes * 0.5)
++ const userIdentitiesData = identitiesMap.get(user.id) || []
++ const avatarUrl = getUserAvatarUrl(
++ {
++ id: user.id,
++ name: user.name,
++ username: user.username,
++ email: user.email,
++ avatarProvider: user.avatarProvider,
++ avatarProviderUserId: user.avatarProviderUserId
++ },
++ userIdentitiesData
++ )
+
+- const userStat = {
++ return {
+ id: user.id,
+- name: user.name || user.username, // 浼樺厛鏄剧ず鐪熷疄濮撳悕锛屽鏋滄病鏈夊垯鏄剧ず鐧诲綍鍚?+ name: user.name || user.username,
+ contributions,
+ likes,
+- activityScore
++ activityScore,
++ avatarUrl
+ }
+-
+- return userStat
+ })
+
+- // 鎸夋椿璺冨害鍒嗘暟闄嶅簭鎺掑垪骞堕檺鍒舵暟閲? const sortedUsers = userStats.sort((a, b) => b.activityScore - a.activityScore).slice(0, limit)
+
+- // 缂撳瓨缁撴灉
+ await cacheService.setActiveUsersStats(semester, limit, sortedUsers)
+ console.log('[Cache] 娲昏穬鐢ㄦ埛缁熻鏁版嵁宸茬紦瀛?)
+
+ return sortedUsers
+ } catch (error) {
+- // 杩斿洖绌烘暟缁勮€屼笉鏄姏鍑洪敊璇紝閬垮厤鍓嶇鏄剧ず閿欒
++ console.error('[Stats] 鑾峰彇娲昏穬鐢ㄦ埛澶辫触:', error)
+ return []
+ }
+ })
+diff --git a/server/api/admin/stats/realtime.get.ts b/server/api/admin/stats/realtime.get.ts
+index 030e2a8..7b4f449 100644
+--- a/server/api/admin/stats/realtime.get.ts
++++ b/server/api/admin/stats/realtime.get.ts
+@@ -1,12 +1,24 @@
+ import { createError, defineEventHandler } from 'h3'
+ import { db } from '~/drizzle/db'
+-import { CacheService } from '../../../services/cacheService'
+-import { songs, users, votes } from '~/drizzle/schema'
+-import { count, eq, gte } from 'drizzle-orm'
++import { CacheService } from '~~/server/services/cacheService'
++import { songs, users, votes, userIdentities } from '~/drizzle/schema'
++import { count, eq, gte, inArray } from 'drizzle-orm'
+ import { getBeijingHour, getBeijingStartOfDay } from '~/utils/timeUtils'
++import { getUserAvatarUrl } from '~~/server/utils/user-avatar'
++
++function formatRelativeTime(date: Date | null | undefined): string {
++ if (!date) return '鏈煡'
++ const now = new Date()
++ const diff = now.getTime() - new Date(date).getTime()
++ const minutes = Math.floor(diff / 60000)
++ if (minutes < 1) return '鍒氬垰'
++ if (minutes < 60) return `${minutes}鍒嗛挓鍓峘
++ const hours = Math.floor(minutes / 60)
++ if (hours < 24) return `${hours}灏忔椂鍓峘
++ return `${Math.floor(hours / 24)}澶╁墠`
++}
+
+ export default defineEventHandler(async (event) => {
+- // 妫€鏌ヨ璇佸拰鏉冮檺
+ const user = event.context.user
+ if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
+ throw createError({
+@@ -16,97 +28,172 @@ export default defineEventHandler(async (event) => {
+ }
+
+ try {
+- // 灏濊瘯浠庣紦瀛樿幏鍙栨暟鎹? const cacheService = CacheService.getInstance()
+ const cachedStats = await cacheService.getRealtimeStats()
+ if (cachedStats) {
+ console.log('[Cache] 瀹炴椂缁熻鏁版嵁缂撳瓨鍛戒腑')
+ return cachedStats
+ }
+- // 鑾峰彇褰撳墠鏃堕棿鐩稿叧鐨勬棩鏈? const now = new Date()
+- const today = getBeijingStartOfDay() // 浣跨敤鍖椾含鏃堕棿鐨勪竴澶╁紑濮?+ const today = getBeijingStartOfDay()
+ const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000)
+ const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000)
+
+- // 骞惰鑾峰彇瀹炴椂缁熻鏁版嵁
+ const [activeUsersData, todayRequests, popularGenres, peakHours] = await Promise.all([
+- // 娲昏穬鐢ㄦ埛鏁板拰鐢ㄦ埛鍒楄〃 (鏈€杩?灏忔椂鐐规瓕銆佺櫥褰曟垨鐐硅禐鐨勭敤鎴?
+ (async () => {
+ try {
+- // 鑾峰彇鏈€杩?灏忔椂鍐呯偣姝岀殑鐢ㄦ埛
+ const recentSongUsers = await db
+ .select({
+ requesterId: songs.requesterId,
+ requesterName: users.name,
+ requesterUsername: users.username,
+- requesterId2: users.id
++ requesterId2: users.id,
++ requesterLastActive: songs.createdAt,
++ requesterAvatarProvider: users.avatarProvider,
++ requesterAvatarProviderUserId: users.avatarProviderUserId,
++ requesterEmail: users.email
+ })
+ .from(songs)
+ .innerJoin(users, eq(songs.requesterId, users.id))
+ .where(gte(songs.createdAt, oneHourAgo))
+- .groupBy(songs.requesterId, users.id, users.name, users.username)
++ .groupBy(songs.requesterId, users.id, users.name, users.username, users.lastLogin, users.avatarProvider, users.avatarProviderUserId, users.email, songs.createdAt)
+
+- // 鑾峰彇鏈€杩?灏忔椂鍐呯櫥褰曠殑鐢ㄦ埛
+ const recentLoginUsers = await db
+ .select({
+ id: users.id,
+ username: users.username,
+- name: users.name
++ name: users.name,
++ lastLogin: users.lastLogin,
++ avatarProvider: users.avatarProvider,
++ avatarProviderUserId: users.avatarProviderUserId,
++ email: users.email
+ })
+ .from(users)
+ .where(gte(users.lastLogin, oneHourAgo))
+
+- // 鑾峰彇鏈€杩?灏忔椂鍐呯偣璧炶繃姝屾洸鐨勭敤鎴? const recentVoteUsers = await db
+ .select({
+ userId: votes.userId,
+ userName: users.name,
+ userUsername: users.username,
+- userId2: users.id
++ userId2: users.id,
++ voteCreatedAt: votes.createdAt,
++ userAvatarProvider: users.avatarProvider,
++ userAvatarProviderUserId: users.avatarProviderUserId,
++ userEmail: users.email
+ })
+ .from(votes)
+ .innerJoin(users, eq(votes.userId, users.id))
+ .where(gte(votes.createdAt, oneHourAgo))
+- .groupBy(votes.userId, users.id, users.name, users.username)
++ .groupBy(votes.userId, users.id, users.name, users.username, users.avatarProvider, users.avatarProviderUserId, users.email, votes.createdAt)
+
+- // 鍚堝苟骞跺幓閲嶇敤鎴峰垪琛?- const userMap = new Map()
++ const userMap = new Map()
+
+- // 娣诲姞鐐规瓕鐢ㄦ埛
+ recentSongUsers.forEach((song) => {
+- userMap.set(song.requesterId2, {
+- id: song.requesterId2,
++ const id = song.requesterId2
++ const existing = userMap.get(id)
++ const lastActive = existing?.lastActive
++ ? new Date(existing.lastActive) > new Date(song.requesterLastActive)
++ ? existing.lastActive
++ : song.requesterLastActive
++ : song.requesterLastActive
++ userMap.set(id, {
++ id,
+ username: song.requesterUsername,
+- name: song.requesterName
++ name: song.requesterName,
++ email: song.requesterEmail,
++ avatarProvider: song.requesterAvatarProvider,
++ avatarProviderUserId: song.requesterAvatarProviderUserId,
++ lastActive
+ })
+ })
+
+- // 娣诲姞鐧诲綍鐢ㄦ埛
+- recentLoginUsers.forEach((user) => {
+- userMap.set(user.id, {
+- id: user.id,
+- username: user.username,
+- name: user.name
++ recentLoginUsers.forEach((u) => {
++ const existing = userMap.get(u.id)
++ const lastActive = existing?.lastActive
++ ? new Date(existing.lastActive) > new Date(u.lastLogin)
++ ? existing.lastActive
++ : u.lastLogin
++ : u.lastLogin
++ userMap.set(u.id, {
++ id: u.id,
++ username: u.username,
++ name: u.name,
++ email: u.email,
++ avatarProvider: u.avatarProvider,
++ avatarProviderUserId: u.avatarProviderUserId,
++ lastActive
+ })
+ })
+
+- // 娣诲姞鐐硅禐鐢ㄦ埛
+ recentVoteUsers.forEach((vote) => {
+- userMap.set(vote.userId2, {
+- id: vote.userId2,
++ const id = vote.userId2
++ const existing = userMap.get(id)
++ const lastActive = existing?.lastActive
++ ? new Date(existing.lastActive) > new Date(vote.voteCreatedAt)
++ ? existing.lastActive
++ : vote.voteCreatedAt
++ : vote.voteCreatedAt
++ userMap.set(id, {
++ id,
+ username: vote.userUsername,
+- name: vote.userName
++ name: vote.userName,
++ email: vote.userEmail,
++ avatarProvider: vote.userAvatarProvider,
++ avatarProviderUserId: vote.userAvatarProviderUserId,
++ lastActive
+ })
+ })
+
+- const activeUsersList = Array.from(userMap.values())
++ const userIds = Array.from(userMap.keys())
++ let identitiesData: any[] = []
++ if (userIds.length > 0) {
++ try {
++ identitiesData = await db
++ .select()
++ .from(userIdentities)
++ .where(inArray(userIdentities.userId, userIds))
++ } catch (e) {
++ identitiesData = []
++ }
++ }
++
++ const identitiesMap = new Map()
++ identitiesData.forEach((identity) => {
++ if (!identitiesMap.has(identity.userId)) {
++ identitiesMap.set(identity.userId, [])
++ }
++ identitiesMap.get(identity.userId)!.push(identity)
++ })
++
++ const activeUsersList = Array.from(userMap.values()).map((u) => {
++ const userIdentitiesData = identitiesMap.get(u.id) || []
++ const avatarUrl = getUserAvatarUrl(
++ {
++ id: u.id,
++ name: u.name,
++ username: u.username,
++ email: u.email,
++ avatarProvider: u.avatarProvider,
++ avatarProviderUserId: u.avatarProviderUserId
++ },
++ userIdentitiesData
++ )
++ return {
++ id: u.id,
++ username: u.username,
++ name: u.name,
++ avatarUrl,
++ lastActive: formatRelativeTime(u.lastActive)
++ }
++ })
+
+ return {
+ count: activeUsersList.length,
+ users: activeUsersList
+ }
+ } catch (error) {
++ console.error('[Realtime] 鑾峰彇娲昏穬鐢ㄦ埛澶辫触:', error)
+ return {
+ count: 0,
+ users: []
+@@ -114,7 +201,6 @@ export default defineEventHandler(async (event) => {
+ }
+ })(),
+
+- // 浠婃棩鐐规瓕鏁? (async () => {
+ try {
+ const todayCountResult = await db
+@@ -127,12 +213,10 @@ export default defineEventHandler(async (event) => {
+ }
+ })(),
+
+- // 鐑棬娴佹淳 (Song妯″瀷涓病鏈塯enre瀛楁锛岃繑鍥炵┖鏁扮粍)
+ (async () => {
+ return []
+ })(),
+
+- // 楂樺嘲鏃舵 (鏈€杩?澶╃殑灏忔椂缁熻)
+ (async () => {
+ try {
+ const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
+@@ -143,7 +227,6 @@ export default defineEventHandler(async (event) => {
+ .from(songs)
+ .where(gte(songs.createdAt, sevenDaysAgo))
+
+- // 鎸夊皬鏃剁粺璁? const hourCount = songsData.reduce(
+ (acc, song) => {
+ const hour = getBeijingHour(song.createdAt)
+@@ -153,7 +236,6 @@ export default defineEventHandler(async (event) => {
+ {} as Record
+ )
+
+- // 杩斿洖鍓?涓珮宄版椂娈? return Object.entries(hourCount)
+ .sort(([, a], [, b]) => b - a)
+ .slice(0, 3)
+@@ -177,12 +259,12 @@ export default defineEventHandler(async (event) => {
+ timestamp: now.toISOString()
+ }
+
+- // 缂撳瓨缁撴灉
+ await cacheService.setRealtimeStats(result)
+ console.log('[Cache] 瀹炴椂缁熻鏁版嵁宸茬紦瀛?)
+
+ return result
+ } catch (error) {
++ console.error('[Realtime] 鑾峰彇澶辫触:', error)
+ throw createError({
+ statusCode: 500,
+ message: '鑾峰彇瀹炴椂缁熻鏁版嵁澶辫触'
+diff --git a/server/api/admin/system-settings/index.post.ts b/server/api/admin/system-settings/index.post.ts
+index b33c4fe..1c25235 100644
+--- a/server/api/admin/system-settings/index.post.ts
++++ b/server/api/admin/system-settings/index.post.ts
+@@ -198,6 +198,520 @@ export default defineEventHandler(async (event) => {
+ updateData.forceBlockAllRequests = body.forceBlockAllRequests
+ }
+
++ if (body.submissionNoteRequiresApproval !== undefined) {
++ if (typeof body.submissionNoteRequiresApproval !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionNoteRequiresApproval 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionNoteRequiresApproval = body.submissionNoteRequiresApproval
++ }
++
++ if (body.telemetryEnabled !== undefined) {
++ if (typeof body.telemetryEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'telemetryEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.telemetryEnabled = body.telemetryEnabled
++ }
++
++ if (body.showBeianIcon !== undefined) {
++ if (typeof body.showBeianIcon !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'showBeianIcon 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.showBeianIcon = body.showBeianIcon
++ }
++
++ if (body.scheduleDaysDisplay !== undefined) {
++ if (body.scheduleDaysDisplay !== null && (!Number.isInteger(body.scheduleDaysDisplay) || body.scheduleDaysDisplay < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysDisplay 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.scheduleDaysDisplay = body.scheduleDaysDisplay
++ }
++
++ if (body.scheduleDaysEditable !== undefined) {
++ if (body.scheduleDaysEditable !== null && (!Number.isInteger(body.scheduleDaysEditable) || body.scheduleDaysEditable < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysEditable 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.scheduleDaysEditable = body.scheduleDaysEditable
++ }
++
++ if (body.scheduleDaysRequestable !== undefined) {
++ if (body.scheduleDaysRequestable !== null && (!Number.isInteger(body.scheduleDaysRequestable) || body.scheduleDaysRequestable < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysRequestable 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.scheduleDaysRequestable = body.scheduleDaysRequestable
++ }
++
++ if (body.scheduleDaysBeforeEnabled !== undefined) {
++ if (typeof body.scheduleDaysBeforeEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysBeforeEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.scheduleDaysBeforeEnabled = body.scheduleDaysBeforeEnabled
++ }
++
++ if (body.scheduleDaysBefore !== undefined) {
++ if (body.scheduleDaysBefore !== null && (!Number.isInteger(body.scheduleDaysBefore) || body.scheduleDaysBefore < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysBefore 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.scheduleDaysBefore = body.scheduleDaysBefore
++ }
++
++ if (body.scheduleDaysAfterEnabled !== undefined) {
++ if (typeof body.scheduleDaysAfterEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysAfterEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.scheduleDaysAfterEnabled = body.scheduleDaysAfterEnabled
++ }
++
++ if (body.scheduleDaysAfter !== undefined) {
++ if (body.scheduleDaysAfter !== null && (!Number.isInteger(body.scheduleDaysAfter) || body.scheduleDaysAfter < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'scheduleDaysAfter 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.scheduleDaysAfter = body.scheduleDaysAfter
++ }
++
++ if (body.forcePasswordChangeOnFirstLogin !== undefined) {
++ if (typeof body.forcePasswordChangeOnFirstLogin !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'forcePasswordChangeOnFirstLogin 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.forcePasswordChangeOnFirstLogin = body.forcePasswordChangeOnFirstLogin
++ }
++
++ if (body.enableSubmissionRestriction !== undefined) {
++ if (typeof body.enableSubmissionRestriction !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'enableSubmissionRestriction 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.enableSubmissionRestriction = body.enableSubmissionRestriction
++ }
++
++ if (body.submissionRestrictionScope !== undefined) {
++ updateData.submissionRestrictionScope = body.submissionRestrictionScope
++ }
++
++ if (body.cardCodeEnabled !== undefined) {
++ if (typeof body.cardCodeEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'cardCodeEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.cardCodeEnabled = body.cardCodeEnabled
++ }
++
++ if (body.cardCodeSubmissionRequired !== undefined) {
++ if (typeof body.cardCodeSubmissionRequired !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'cardCodeSubmissionRequired 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.cardCodeSubmissionRequired = body.cardCodeSubmissionRequired
++ }
++
++ if (body.cardCodeVoteEnabled !== undefined) {
++ if (typeof body.cardCodeVoteEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'cardCodeVoteEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.cardCodeVoteEnabled = body.cardCodeVoteEnabled
++ }
++
++ if (body.cardCodeReplayEnabled !== undefined) {
++ if (typeof body.cardCodeReplayEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'cardCodeReplayEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.cardCodeReplayEnabled = body.cardCodeReplayEnabled
++ }
++
++ if (body.enableCardCodeRequests !== undefined) {
++ if (typeof body.enableCardCodeRequests !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'enableCardCodeRequests 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.enableCardCodeRequests = body.enableCardCodeRequests
++ }
++
++ if (body.requireCardCodeForRequests !== undefined) {
++ if (typeof body.requireCardCodeForRequests !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'requireCardCodeForRequests 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.requireCardCodeForRequests = body.requireCardCodeForRequests
++ }
++
++ if (body.enableCardCodeLimitBypass !== undefined) {
++ if (typeof body.enableCardCodeLimitBypass !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'enableCardCodeLimitBypass 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.enableCardCodeLimitBypass = body.enableCardCodeLimitBypass
++ }
++
++ if (body.submissionRestrictionEnabled !== undefined) {
++ if (typeof body.submissionRestrictionEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionRestrictionEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionRestrictionEnabled = body.submissionRestrictionEnabled
++ }
++
++ if (body.submissionRestrictionGradeEnabled !== undefined) {
++ if (typeof body.submissionRestrictionGradeEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionRestrictionGradeEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionRestrictionGradeEnabled = body.submissionRestrictionGradeEnabled
++ }
++
++ if (body.submissionRestrictionClassEnabled !== undefined) {
++ if (typeof body.submissionRestrictionClassEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionRestrictionClassEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionRestrictionClassEnabled = body.submissionRestrictionClassEnabled
++ }
++
++ if (body.submissionRestrictionGraduateEnabled !== undefined) {
++ if (typeof body.submissionRestrictionGraduateEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionRestrictionGraduateEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionRestrictionGraduateEnabled = body.submissionRestrictionGraduateEnabled
++ }
++
++ if (body.submissionRestrictionWithdrawnEnabled !== undefined) {
++ if (typeof body.submissionRestrictionWithdrawnEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'submissionRestrictionWithdrawnEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.submissionRestrictionWithdrawnEnabled = body.submissionRestrictionWithdrawnEnabled
++ }
++
++ if (body.captchaProvider !== undefined) {
++ updateData.captchaProvider = body.captchaProvider
++ }
++
++ if (body.captchaEnabled !== undefined) {
++ if (typeof body.captchaEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'captchaEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.captchaEnabled = body.captchaEnabled
++ }
++
++ if (body.allowRegister !== undefined) {
++ if (typeof body.allowRegister !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'allowRegister 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.allowRegister = body.allowRegister
++ }
++
++ if (body.registerRequireEmail !== undefined) {
++ if (typeof body.registerRequireEmail !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'registerRequireEmail 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.registerRequireEmail = body.registerRequireEmail
++ }
++
++ if (body.registerRequireEmailVerify !== undefined) {
++ if (typeof body.registerRequireEmailVerify !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'registerRequireEmailVerify 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.registerRequireEmailVerify = body.registerRequireEmailVerify
++ }
++
++ if (body.registerDefaultRole !== undefined) {
++ updateData.registerDefaultRole = body.registerDefaultRole
++ }
++
++ if (body.registerRequiresApproval !== undefined) {
++ if (typeof body.registerRequiresApproval !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'registerRequiresApproval 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.registerRequiresApproval = body.registerRequiresApproval
++ }
++
++ if (body.oauthRegisterRequiresApproval !== undefined) {
++ if (typeof body.oauthRegisterRequiresApproval !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'oauthRegisterRequiresApproval 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.oauthRegisterRequiresApproval = body.oauthRegisterRequiresApproval
++ }
++
++ if (body.registerEmailRequired !== undefined) {
++ if (typeof body.registerEmailRequired !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'registerEmailRequired 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.registerEmailRequired = body.registerEmailRequired
++ }
++
++ if (body.registerRequiresGradeClass !== undefined) {
++ if (typeof body.registerRequiresGradeClass !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'registerRequiresGradeClass 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.registerRequiresGradeClass = body.registerRequiresGradeClass
++ }
++
++ if (body.oauthEnabled !== undefined) {
++ if (typeof body.oauthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'oauthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.oauthEnabled = body.oauthEnabled
++ }
++
++ if (body.oauthAllowRegister !== undefined) {
++ if (typeof body.oauthAllowRegister !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'oauthAllowRegister 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.oauthAllowRegister = body.oauthAllowRegister
++ }
++
++ if (body.oauthAllowLogin !== undefined) {
++ if (typeof body.oauthAllowLogin !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'oauthAllowLogin 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.oauthAllowLogin = body.oauthAllowLogin
++ }
++
++ if (body.oauthAllowBind !== undefined) {
++ if (typeof body.oauthAllowBind !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'oauthAllowBind 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.oauthAllowBind = body.oauthAllowBind
++ }
++
++ if (body.allowOAuthRegistration !== undefined) {
++ if (typeof body.allowOAuthRegistration !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'allowOAuthRegistration 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.allowOAuthRegistration = body.allowOAuthRegistration
++ }
++
++ if (body.oauthProviders !== undefined) {
++ updateData.oauthProviders = body.oauthProviders
++ }
++
++ if (body.githubOAuthEnabled !== undefined) {
++ if (typeof body.githubOAuthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'githubOAuthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.githubOAuthEnabled = body.githubOAuthEnabled
++ }
++
++ if (body.casdoorOAuthEnabled !== undefined) {
++ if (typeof body.casdoorOAuthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'casdoorOAuthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.casdoorOAuthEnabled = body.casdoorOAuthEnabled
++ }
++
++ if (body.googleOAuthEnabled !== undefined) {
++ if (typeof body.googleOAuthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'googleOAuthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.googleOAuthEnabled = body.googleOAuthEnabled
++ }
++
++ if (body.aggregateOAuthEnabled !== undefined) {
++ if (typeof body.aggregateOAuthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'aggregateOAuthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.aggregateOAuthEnabled = body.aggregateOAuthEnabled
++ }
++
++ if (body.aggregateOAuthLoginType !== undefined) {
++ updateData.aggregateOAuthLoginType = body.aggregateOAuthLoginType
++ }
++
++ if (body.customOAuthEnabled !== undefined) {
++ if (typeof body.customOAuthEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'customOAuthEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.customOAuthEnabled = body.customOAuthEnabled
++ }
++
++ if (body.captchaMaxFailures !== undefined) {
++ if (body.captchaMaxFailures !== null && (!Number.isInteger(body.captchaMaxFailures) || body.captchaMaxFailures < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'captchaMaxFailures 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.captchaMaxFailures = body.captchaMaxFailures
++ }
++
++ if (body.autoBackupEnabled !== undefined) {
++ if (typeof body.autoBackupEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'autoBackupEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.autoBackupEnabled = body.autoBackupEnabled
++ }
++
++ if (body.autoBackupIntervalHours !== undefined) {
++ if (body.autoBackupIntervalHours !== null && (!Number.isInteger(body.autoBackupIntervalHours) || body.autoBackupIntervalHours < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'autoBackupIntervalHours 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.autoBackupIntervalHours = body.autoBackupIntervalHours
++ }
++
++ if (body.autoBackupRetentionDays !== undefined) {
++ if (body.autoBackupRetentionDays !== null && (!Number.isInteger(body.autoBackupRetentionDays) || body.autoBackupRetentionDays < 0)) {
++ throw createError({
++ statusCode: 400,
++ message: 'autoBackupRetentionDays 蹇呴』鏄潪璐熸暣鏁版垨null'
++ })
++ }
++ updateData.autoBackupRetentionDays = body.autoBackupRetentionDays
++ }
++
++ if (body.statisticsCodeEnabled !== undefined) {
++ if (typeof body.statisticsCodeEnabled !== 'boolean') {
++ throw createError({
++ statusCode: 400,
++ message: 'statisticsCodeEnabled 蹇呴』鏄竷灏斿€?
++ })
++ }
++ updateData.statisticsCodeEnabled = body.statisticsCodeEnabled
++ }
++
++ if (body.statisticsCodeBody !== undefined) {
++ updateData.statisticsCodeBody = body.statisticsCodeBody
++ }
++
++ if (body.statisticsCodeHead !== undefined) {
++ updateData.statisticsCodeHead = body.statisticsCodeHead
++ }
++
++ if (body.defaultTheme !== undefined) {
++ updateData.defaultTheme =
++ typeof body.defaultTheme === 'string' && body.defaultTheme.trim()
++ ? body.defaultTheme.trim()
++ : null
++ }
++
++ if (body.enabledThemes !== undefined) {
++ updateData.enabledThemes =
++ body.enabledThemes === null || body.enabledThemes === undefined
++ ? null
++ : typeof body.enabledThemes === 'string'
++ ? body.enabledThemes
++ : JSON.stringify(body.enabledThemes)
++ }
++
+ // SMTP閰嶇疆瀛楁
+ if (body.smtpEnabled !== undefined) {
+ if (typeof body.smtpEnabled !== 'boolean') {
+@@ -266,10 +780,69 @@ export default defineEventHandler(async (event) => {
+ })
+ }
+
++ const DEFAULTS = {
++ submissionNoteRequiresApproval: false,
++ telemetryEnabled: true,
++ showBeianIcon: true,
++ scheduleDaysDisplay: 7,
++ scheduleDaysEditable: 3,
++ scheduleDaysRequestable: 14,
++ scheduleDaysBeforeEnabled: false,
++ scheduleDaysBefore: 1,
++ scheduleDaysAfterEnabled: false,
++ scheduleDaysAfter: 1,
++ forcePasswordChangeOnFirstLogin: false,
++ enableSubmissionRestriction: false,
++ submissionRestrictionScope: 'all',
++ cardCodeEnabled: false,
++ cardCodeSubmissionRequired: false,
++ cardCodeVoteEnabled: false,
++ cardCodeReplayEnabled: false,
++ enableCardCodeRequests: false,
++ requireCardCodeForRequests: false,
++ enableCardCodeLimitBypass: false,
++ submissionRestrictionEnabled: false,
++ submissionRestrictionGradeEnabled: false,
++ submissionRestrictionClassEnabled: false,
++ submissionRestrictionGraduateEnabled: false,
++ submissionRestrictionWithdrawnEnabled: false,
++ captchaProvider: 'TURNSTILE',
++ captchaEnabled: false,
++ allowRegister: true,
++ registerRequireEmail: false,
++ registerRequireEmailVerify: false,
++ registerDefaultRole: 'USER',
++ registerRequiresApproval: false,
++ oauthRegisterRequiresApproval: false,
++ registerEmailRequired: false,
++ registerRequiresGradeClass: false,
++ oauthEnabled: false,
++ oauthAllowRegister: true,
++ oauthAllowLogin: true,
++ oauthAllowBind: true,
++ allowOAuthRegistration: false,
++ oauthProviders: '[]',
++ githubOAuthEnabled: false,
++ casdoorOAuthEnabled: false,
++ googleOAuthEnabled: false,
++ aggregateOAuthEnabled: false,
++ aggregateOAuthLoginType: 'qq',
++ customOAuthEnabled: false,
++ captchaMaxFailures: 3,
++ autoBackupEnabled: false,
++ autoBackupIntervalHours: 24,
++ autoBackupRetentionDays: 30,
++ statisticsCodeEnabled: false,
++ defaultTheme: 'ClassicLight',
++ enabledThemes: '["ClassicLight","ModernLight"]'
++} as const
++
+ // 鑾峰彇褰撳墠璁剧疆锛屽鏋滀笉瀛樺湪鍒欏垱寤? const settingsResult = await db.select().from(systemSettings).limit(1)
+ let settings = settingsResult[0] || null
+
++ const fillDefaults = (obj: any) => ({ ...obj, ...Object.fromEntries(Object.entries(DEFAULTS).filter(([k]) => !Object.prototype.hasOwnProperty.call(obj || {}, k) || (obj && obj[k] === undefined || obj[k] === null))) })
++
+ if (!settings) {
+ // 濡傛灉涓嶅瓨鍦紝鍒涘缓鏂拌缃? const newSettingsResult = await db
+@@ -324,7 +897,7 @@ export default defineEventHandler(async (event) => {
+ .set(updateData)
+ .where(eq(systemSettings.id, settings.id))
+ .returning()
+- settings = updatedSettingsResult[0]
++ settings = fillDefaults(updatedSettingsResult[0])
+ }
+
+ // 娓呴櫎绯荤粺璁剧疆缂撳瓨
+diff --git a/server/api/admin/system-settings/index.ts b/server/api/admin/system-settings/index.ts
+index cbe9256..8603bf9 100644
+--- a/server/api/admin/system-settings/index.ts
++++ b/server/api/admin/system-settings/index.ts
+@@ -1,6 +1,7 @@
+ import { db } from '~/drizzle/db'
+ import { systemSettings } from '~/drizzle/schema'
+ import { CacheService } from '../../../services/cacheService'
++import { SYSTEM_SETTINGS_DEFAULTS } from '~~/server/utils/system-settings-defaults'
+
+ export default defineEventHandler(async (event) => {
+ // 妫€鏌ョ敤鎴疯璇佸拰鏉冮檺
+@@ -66,13 +67,15 @@ export default defineEventHandler(async (event) => {
+
+ // 灏嗚缃瓨鍏ョ紦瀛? try {
++ const merged: any = { ...SYSTEM_SETTINGS_DEFAULTS, ...(settings || {}) }
++ settings = merged
+ await cacheService.setSystemSettings(settings)
+ } catch (cacheError) {
+ console.warn('缂撳瓨绯荤粺璁剧疆澶辫触:', cacheError)
+ }
+
+ // 闅愯棌鏁忔劅瀛楁
+- if (settings.smtpPassword) {
++ if (settings && settings.smtpPassword) {
+ settings.smtpPassword = '****************'
+ }
+
+diff --git a/server/api/admin/users/[id].put.ts b/server/api/admin/users/[id].put.ts
+index a9cc874..4dbfec8 100644
+--- a/server/api/admin/users/[id].put.ts
++++ b/server/api/admin/users/[id].put.ts
+@@ -3,6 +3,7 @@ import { db } from '~/drizzle/db'
+ import { users, userStatusLogs } from '~/drizzle/schema'
+ import { eq } from 'drizzle-orm'
+ import { updateUserPassword } from '~~/server/services/userService'
++import { getRoleLevel, canChangeRoleOf, canResetPasswordFor } from '~~/server/utils/permissions'
+
+ export default defineEventHandler(async (event) => {
+ try {
+@@ -51,6 +52,8 @@ export default defineEventHandler(async (event) => {
+ }
+
+ const targetUser = existingUser[0]
++ const currentLevel = getRoleLevel(user.role)
++ const targetLevel = getRoleLevel(targetUser.role)
+
+ // 1. 绂佹淇敼绯荤粺鍒濆瓒呯骇绠$悊鍛?(ID: 1)
+ if (targetUser.id === 1) {
+@@ -61,7 +64,6 @@ export default defineEventHandler(async (event) => {
+ }
+
+ // 2. 绂佹瀵硅嚜韬繘琛屼换浣曠敤鎴风鐞嗘搷浣滐紙鍖呮嫭瑙掕壊銆佽祫鏂欍€佺姸鎬併€佸瘑鐮佺瓑锛?- // 浣跨敤 String 杞崲纭繚 ID 姣旇緝鐨勫噯纭€? if (String(userId) === String(user.id)) {
+ throw createError({
+ statusCode: 400,
+@@ -69,12 +71,11 @@ export default defineEventHandler(async (event) => {
+ })
+ }
+
+- // 3. 瓒婄骇淇敼淇濇姢
+- // 濡傛灉鐩爣鐢ㄦ埛鏄?SUPER_ADMIN锛屾搷浣滆€呭繀椤绘槸 SUPER_ADMIN
+- if (targetUser.role === 'SUPER_ADMIN' && user.role !== 'SUPER_ADMIN') {
++ // 3. 闈炶秴绾х鐞嗗憳蹇呴』涓ユ牸楂樹簬鐩爣瑙掕壊绾у埆
++ if (user.role !== 'SUPER_ADMIN' && currentLevel <= targetLevel) {
+ throw createError({
+ statusCode: 403,
+- statusMessage: '鏉冮檺涓嶈冻锛氭櫘閫氱鐞嗗憳鏃犳硶淇敼瓒呯骇绠$悊鍛樹俊鎭?
++ statusMessage: `鏉冮檺涓嶈冻锛?{user.role} 鏃犳硶鎿嶄綔鍚岀骇鎴栨洿楂樿鑹茬殑鐢ㄦ埛`
+ })
+ }
+
+@@ -94,31 +95,26 @@ export default defineEventHandler(async (event) => {
+ }
+ }
+
+- // 瑙掕壊鏉冮檺鎺у埗
+- let validRole = 'USER'
+- if (role && ['USER', 'TEACHER', 'ADMIN', 'SONG_ADMIN', 'SUPER_ADMIN'].includes(role)) {
+- // 瓒呯骇绠$悊鍛樺彲浠ヨ缃换浣曡鑹?- if (user.role === 'SUPER_ADMIN') {
+- validRole = role
+- }
+- // 绠$悊鍛樺彧鑳借缃鐞嗗憳浠ヤ笅鐨勮鑹诧紙USER, SONG_ADMIN锛?- else if (user.role === 'ADMIN') {
+- if (['USER', 'TEACHER', 'SONG_ADMIN'].includes(role)) {
+- validRole = role
+- } else {
+- throw createError({
+- statusCode: 403,
+- statusMessage: '绠$悊鍛樺彧鑳借缃敤鎴枫€佹暀甯堝拰姝屾洸绠$悊鍛樿鑹?
+- })
+- }
+- }
+- // 鍏朵粬瑙掕壊涓嶈兘璁剧疆瑙掕壊
+- else {
++ // 瑙掕壊鏉冮檺鎺у埗锛堢粺涓€浣跨敤 canChangeRoleOf 璇箟鍖栨鏌ワ級
++ let validRole = targetUser.role
++ if (role && role !== targetUser.role) {
++ if (!canChangeRoleOf(user, targetUser, role)) {
+ throw createError({
+ statusCode: 403,
+- statusMessage: '娌℃湁鏉冮檺璁剧疆鐢ㄦ埛瑙掕壊'
++ statusMessage: user.role === 'SUPER_ADMIN'
++ ? '瓒呯骇绠$悊鍛樹笉鑳藉皢鐩爣鐢ㄦ埛鎻愬崌涓?SUPER_ADMIN'
++ : `鏉冮檺涓嶈冻锛?{user.role} 鏃犳硶灏嗙敤鎴疯鑹叉洿鏀逛负 ${role}`
+ })
+ }
++ validRole = role
++ }
++
++ // 濡傛灉璇锋眰涓寘鍚瘑鐮佹洿鏂帮紝鏍¢獙瀵嗙爜閲嶇疆鏉冮檺
++ if (password && !canResetPasswordFor(user, targetUser)) {
++ throw createError({
++ statusCode: 403,
++ statusMessage: `鏉冮檺涓嶈冻锛?{user.role} 鏃犳硶閲嶇疆鐩爣鐢ㄦ埛鐨勫瘑鐮乣
++ })
+ }
+
+ // 楠岃瘉status瀛楁鐨勬湁鏁堟€?diff --git a/server/api/admin/users/[id]/reset-password.post.ts b/server/api/admin/users/[id]/reset-password.post.ts
+index 18e9cc7..c421e5b 100644
+--- a/server/api/admin/users/[id]/reset-password.post.ts
++++ b/server/api/admin/users/[id]/reset-password.post.ts
+@@ -1,16 +1,18 @@
+ import { updateUserPassword } from '~~/server/services/userService'
++import { db } from '~/drizzle/db'
++import { users } from '~/drizzle/schema'
++import { eq } from 'drizzle-orm'
++import { canResetPasswordFor, getRoleLevel } from '~~/server/utils/permissions'
+
+ export default defineEventHandler(async (event) => {
+- // 妫€鏌ヨ璇佸拰鏉冮檺
+- const user = event.context.user
+- if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
++ const currentUser = event.context.user
++ if (!currentUser || !['ADMIN', 'SUPER_ADMIN'].includes(currentUser.role)) {
+ throw createError({
+ statusCode: 403,
+ message: '娌℃湁鏉冮檺璁块棶'
+ })
+ }
+
+- // 瀹夊叏鑾峰彇ID鍙傛暟
+ const params = event.context.params || {}
+ const id = parseInt(params.id || '')
+
+@@ -31,15 +33,54 @@ export default defineEventHandler(async (event) => {
+ }
+
+ try {
+- // 绂佹瀵硅嚜韬繘琛屽瘑鐮侀噸缃紙閫氳繃鐢ㄦ埛绠$悊鐣岄潰锛?- if (id === user.id) {
++ if (id === currentUser.id) {
+ throw createError({
+ statusCode: 400,
+ message: '绂佹鍦ㄧ敤鎴风鐞嗕腑閲嶇疆鑷繁鐨勫瘑鐮?
+ })
+ }
+-
+- // 浣跨敤缁熶竴鏈嶅姟閲嶇疆瀵嗙爜 (forceReset = true)
++
++ const targetUserResult = await db
++ .select({ id: users.id, role: users.role, username: users.username })
++ .from(users)
++ .where(eq(users.id, id))
++ .limit(1)
++
++ if (targetUserResult.length === 0) {
++ throw createError({
++ statusCode: 404,
++ message: '鐢ㄦ埛涓嶅瓨鍦?
++ })
++ }
++
++ const targetUser = targetUserResult[0]
++
++ if (targetUser.id === 1) {
++ throw createError({
++ statusCode: 403,
++ message: '鏃犳硶閲嶇疆绯荤粺鍒濆瓒呯骇绠$悊鍛樼殑瀵嗙爜'
++ })
++ }
++
++ const currentLevel = getRoleLevel(currentUser.role)
++ const targetLevel = getRoleLevel(targetUser.role)
++
++ if (currentUser.role !== 'SUPER_ADMIN') {
++ if (currentLevel <= targetLevel) {
++ throw createError({
++ statusCode: 403,
++ message: '鏉冮檺涓嶈冻锛氫笉鑳介噸缃鑹茬骇鍒ぇ浜庣瓑浜庤嚜宸辩殑鐢ㄦ埛瀵嗙爜'
++ })
++ }
++ }
++
++ if (!canResetPasswordFor(currentUser, targetUser)) {
++ throw createError({
++ statusCode: 403,
++ message: '鏉冮檺涓嶈冻锛氬綋鍓嶈鑹叉棤娉曢噸缃鐢ㄦ埛鐨勫瘑鐮?
++ })
++ }
++
+ await updateUserPassword(id, body.newPassword, true)
+
+ return {
+@@ -47,7 +88,10 @@ export default defineEventHandler(async (event) => {
+ message: '瀵嗙爜閲嶇疆鎴愬姛'
+ }
+ } catch (error) {
+- console.error('閲嶇疆瀵嗙爜澶辫触:', error)
++ console.error('[ResetPassword] 澶辫触:', error)
++ if (error.statusCode) {
++ throw error
++ }
+ throw createError({
+ statusCode: 500,
+ message: '閲嶇疆瀵嗙爜澶辫触'
+diff --git a/server/api/admin/users/index.get.ts b/server/api/admin/users/index.get.ts
+index 950aa84..9c2b06f 100644
+--- a/server/api/admin/users/index.get.ts
++++ b/server/api/admin/users/index.get.ts
+@@ -96,26 +96,36 @@ export default defineEventHandler(async (event) => {
+ meowNickname: true,
+ meowBoundAt: true,
+ createdAt: true,
+- updatedAt: true
++ updatedAt: true,
++ avatarProvider: true,
++ avatarProviderUserId: true,
++ email: true
+ },
+ with: {
+- identities: {
+- columns: {
+- provider: true,
+- providerUsername: true
+- },
+- where: (identities, { eq }) => eq(identities.provider, 'github')
+- }
++ identities: true
+ }
+ })
+
+- // 澶勭悊鐢ㄦ埛鍒楄〃锛屾坊鍔犲ご鍍忓瓧娈?- const formattedUsers = usersList.map((user) => ({
+- ...user,
+- avatar: user.identities?.[0]?.providerUsername
+- ? `https://github.com/${user.identities[0].providerUsername}.png`
+- : null
+- }))
++ const { getUserAvatarUrl } = await import('~~/server/utils/user-avatar')
++
++ const formattedUsers = usersList.map((user) => {
++ const avatarUrl = getUserAvatarUrl(
++ {
++ id: user.id,
++ name: user.name,
++ username: user.username,
++ email: user.email,
++ avatarProvider: user.avatarProvider,
++ avatarProviderUserId: user.avatarProviderUserId
++ },
++ user.identities || []
++ )
++ return {
++ ...user,
++ avatar: avatarUrl,
++ avatarUrl
++ }
++ })
+
+ // 璁$畻鍒嗛〉淇℃伅
+ const totalPages = Math.ceil(total / limitNum)
+diff --git a/server/api/admin/users/index.post.ts b/server/api/admin/users/index.post.ts
+index dd11826..5f41903 100644
+--- a/server/api/admin/users/index.post.ts
++++ b/server/api/admin/users/index.post.ts
+@@ -2,6 +2,7 @@ import bcrypt from 'bcryptjs'
+ import { db } from '~/drizzle/db'
+ import { users } from '~/drizzle/schema'
+ import { eq } from 'drizzle-orm'
++import { getRoleLevel, isRoleHigher, canManageUserRole } from '~~/server/utils/permissions'
+
+ export default defineEventHandler(async (event) => {
+ // 妫€鏌ヨ璇佸拰鏉冮檺
+@@ -51,31 +52,21 @@ export default defineEventHandler(async (event) => {
+ // 鍔犲瘑瀵嗙爜
+ const hashedPassword = await bcrypt.hash(body.password, 10)
+
+- // 瑙掕壊鏉冮檺鎺у埗
++ // 瑙掕壊鏉冮檺鎺у埗锛堢粺涓€浣跨敤 canManageUserRole 鏁板€煎寲绾у埆妫€鏌ワ級
+ let validRole = 'USER'
+- if (body.role && ['USER', 'TEACHER', 'ADMIN', 'SONG_ADMIN', 'SUPER_ADMIN'].includes(body.role)) {
+- // 瓒呯骇绠$悊鍛樺彲浠ュ垱寤轰换浣曡鑹茬殑鐢ㄦ埛
+- if (user.role === 'SUPER_ADMIN') {
+- validRole = body.role
+- }
+- // 绠$悊鍛樺彧鑳藉垱寤虹鐞嗗憳浠ヤ笅鐨勮鑹诧紙USER, SONG_ADMIN锛?- else if (user.role === 'ADMIN') {
+- if (['USER', 'TEACHER', 'SONG_ADMIN'].includes(body.role)) {
+- validRole = body.role
+- } else {
+- throw createError({
+- statusCode: 403,
+- message: '绠$悊鍛樺彧鑳藉垱寤虹敤鎴枫€佹暀甯堝拰姝屾洸绠$悊鍛樿鑹?
+- })
+- }
+- }
+- // 鍏朵粬瑙掕壊涓嶈兘鍒涘缓鐢ㄦ埛
+- else {
++ const requestedRole = body.role
++ if (requestedRole) {
++ if (!canManageUserRole(user, requestedRole)) {
++ const currentLevel = getRoleLevel(user.role)
++ const targetLevel = getRoleLevel(requestedRole)
+ throw createError({
+ statusCode: 403,
+- message: '娌℃湁鏉冮檺鍒涘缓鐢ㄦ埛'
++ message: isRoleHigher(requestedRole, user.role) || currentLevel <= targetLevel
++ ? `鏉冮檺涓嶈冻锛?{user.role} 鏃犳硶鍒涘缓绾у埆楂樹簬鎴栫瓑浜庤嚜韬殑瑙掕壊锛?{requestedRole}锛塦
++ : `娌℃湁鏉冮檺鍒涘缓 ${requestedRole} 瑙掕壊鐨勭敤鎴穈
+ })
+ }
++ validRole = requestedRole
+ }
+
+ // 鐘舵€侀獙璇?diff --git a/server/api/auth/2fa/verify.post.ts b/server/api/auth/2fa/verify.post.ts
+index 14f191c..7f5fd7e 100644
+--- a/server/api/auth/2fa/verify.post.ts
++++ b/server/api/auth/2fa/verify.post.ts
+@@ -3,6 +3,7 @@ const { authenticator } = otplib
+ import { db, userIdentities, eq, and, users } from '~/drizzle/db'
+ import { twoFactorCodes } from '~~/server/utils/twoFactorStore'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+ import { getClientIP } from '~~/server/utils/ip-utils'
+ import { getBeijingTime } from '~/utils/timeUtils'
+
+@@ -100,7 +101,12 @@ export default defineEventHandler(async (event) => {
+ .catch((err) => console.error('Error updating user login info:', err))
+
+ // 鐢熸垚Token
+- const authToken = JWTEnhanced.generateToken(user.id, user.role)
++ const { token: authToken, jti } = JWTEnhanced.generateTokenWithJti(user.id, user.role)
++ await createAuthSession(event, {
++ userId: user.id,
++ jti,
++ loginMethod: type === 'totp' ? 'totp_2fa' : 'email_2fa'
++ })
+
+ const isSecure =
+ getRequestURL(event).protocol === 'https:' ||
+diff --git a/server/api/auth/[provider]/callback.get.ts b/server/api/auth/[provider]/callback.get.ts
+index fe196ca..dc24044 100644
+--- a/server/api/auth/[provider]/callback.get.ts
++++ b/server/api/auth/[provider]/callback.get.ts
+@@ -2,6 +2,7 @@ import { parseState, getRedirectUri } from '~~/server/utils/oauth'
+ import { generateBindingToken } from '~~/server/utils/oauth-token'
+ import { db, eq, userIdentities } from '~/drizzle/db'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+ import { getOAuthStrategy } from '~~/server/utils/oauth-strategies'
+ import { isUserBlocked, getUserBlockRemainingTime } from '~~/server/services/securityService'
+
+@@ -142,7 +143,13 @@ async function handleUserLoginOrBind(
+ }
+
+ // 鐧诲綍
+- const token = JWTEnhanced.generateToken(existingIdentity.user.id, existingIdentity.user.role)
++ const { token, jti } = JWTEnhanced.generateTokenWithJti(existingIdentity.user.id, existingIdentity.user.role)
++ await createAuthSession(event, {
++ userId: existingIdentity.user.id,
++ jti,
++ loginMethod: 'oauth',
++ oauthProvider: provider
++ })
+ setCookie(event, 'auth-token', token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+diff --git a/server/api/auth/bind.post.ts b/server/api/auth/bind.post.ts
+index 1e31b27..43025d1 100644
+--- a/server/api/auth/bind.post.ts
++++ b/server/api/auth/bind.post.ts
+@@ -1,6 +1,7 @@
+ import bcrypt from 'bcryptjs'
+ import { db, eq, users, userIdentities } from '~/drizzle/db'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+ import { verifyBindingToken } from '~~/server/utils/oauth-token'
+ import {
+ isAccountLocked,
+@@ -86,7 +87,13 @@ export default defineEventHandler(async (event) => {
+ recordLoginSuccess(username, clientIp)
+
+ // 鐧诲綍
+- const token = JWTEnhanced.generateToken(user.id, user.role)
++ const { token, jti } = JWTEnhanced.generateTokenWithJti(user.id, user.role)
++ await createAuthSession(event, {
++ userId: user.id,
++ jti,
++ loginMethod: 'oauth',
++ oauthProvider: payload.provider
++ })
+ setCookie(event, 'auth-token', token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+diff --git a/server/api/auth/login.post.ts b/server/api/auth/login.post.ts
+index 5d3f3f9..2113caf 100644
+--- a/server/api/auth/login.post.ts
++++ b/server/api/auth/login.post.ts
+@@ -1,6 +1,7 @@
+ import bcrypt from 'bcryptjs'
+ import { db, eq, users, userIdentities, and } from '~/drizzle/db'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+ import {
+ getAccountLockRemainingTime,
+ getIPBlockRemainingTime,
+@@ -187,7 +188,8 @@ export default defineEventHandler(async (event) => {
+ .catch((err) => console.error('Error updating user login info:', err))
+
+ // 鐢熸垚JWT
+- const token = JWTEnhanced.generateToken(user.id, user.role)
++ const { token, jti } = JWTEnhanced.generateTokenWithJti(user.id, user.role)
++ await createAuthSession(event, { userId: user.id, jti, loginMethod: 'password' })
+
+ // 鑷姩鍒ゆ柇鏄惁闇€瑕乻ecure
+ const isSecure =
+diff --git a/server/api/auth/register.post.ts b/server/api/auth/register.post.ts
+index 5e6ef73..7369928 100644
+--- a/server/api/auth/register.post.ts
++++ b/server/api/auth/register.post.ts
+@@ -1,6 +1,7 @@
+ import bcrypt from 'bcryptjs'
+ import { db, eq, users } from '~/drizzle/db'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+ import { emailVerificationCodes } from '~~/server/api/user/email/send-code.post'
+
+ export default defineEventHandler(async (event) => {
+@@ -109,7 +110,8 @@ export default defineEventHandler(async (event) => {
+ }
+
+ // 绛惧彂 JWT Token
+- const token = JWTEnhanced.generateToken(newUser.id, newUser.role)
++ const { token, jti } = JWTEnhanced.generateTokenWithJti(newUser.id, newUser.role)
++ await createAuthSession(event, { userId: newUser.id, jti, loginMethod: 'password' })
+
+ const isSecure =
+ getRequestURL(event).protocol === 'https:' ||
+diff --git a/server/api/auth/verify.get.ts b/server/api/auth/verify.get.ts
+index d44065b..db98f63 100644
+--- a/server/api/auth/verify.get.ts
++++ b/server/api/auth/verify.get.ts
+@@ -3,6 +3,7 @@ import { db } from '~/drizzle/db'
+ import { users } from '~/drizzle/schema'
+ import { executeRedisCommand, isRedisReady } from '../../utils/redis'
+ import { eq } from 'drizzle-orm'
++import { isSessionRevokedByJti, touchAuthSession } from '~~/server/utils/create-auth-session'
+
+ // 鐢ㄦ埛璁よ瘉缂撳瓨锛堟案涔呯紦瀛橈紝鐧诲嚭鎴栨潈闄愬彉鏇存椂涓诲姩澶辨晥锛?
+@@ -19,9 +20,17 @@ export default defineEventHandler(async (event) => {
+ }
+
+ // 楠岃瘉JWT浠ょ墝
+- const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number }
++ const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number; jti?: string }
+ const userId = decoded.userId
+
++ if (await isSessionRevokedByJti(decoded.jti)) {
++ throw createError({
++ statusCode: 401,
++ statusMessage: '浼氳瘽宸叉挙閿€锛岃閲嶆柊鐧诲綍'
++ })
++ }
++ touchAuthSession(decoded.jti).catch(() => undefined)
++
+ // 浼樺厛浠嶳edis缂撳瓨鑾峰彇鐢ㄦ埛璁よ瘉鐘舵€? if (isRedisReady()) {
+ const cachedUser = await executeRedisCommand(async () => {
+diff --git a/server/api/auth/webauthn/login/verify.post.ts b/server/api/auth/webauthn/login/verify.post.ts
+index 1367427..a05d8d4 100644
+--- a/server/api/auth/webauthn/login/verify.post.ts
++++ b/server/api/auth/webauthn/login/verify.post.ts
+@@ -4,6 +4,7 @@ import { getWebAuthnConfig } from '~~/server/utils/webauthn-config'
+ import { db, eq, and, userIdentities } from '~/drizzle/db'
+ import { createError, defineEventHandler, setCookie } from 'h3'
+ import { JWTEnhanced } from '~~/server/utils/jwt-enhanced'
++import { createAuthSession } from '~~/server/utils/create-auth-session'
+
+ export default defineEventHandler(async (event) => {
+ const body = await readBody(event)
+@@ -84,7 +85,8 @@ export default defineEventHandler(async (event) => {
+ .where(eq(userIdentities.id, identity.id))
+
+ // 绛惧彂鐧诲綍 Token
+- const token = JWTEnhanced.generateToken(identity.user.id, identity.user.role)
++ const { token, jti } = JWTEnhanced.generateTokenWithJti(identity.user.id, identity.user.role)
++ await createAuthSession(event, { userId: identity.user.id, jti, loginMethod: 'webauthn' })
+ setCookie(event, 'auth-token', token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+diff --git a/server/api/blacklist/check.post.ts b/server/api/blacklist/check.post.ts
+index b0a2922..f666348 100644
+--- a/server/api/blacklist/check.post.ts
++++ b/server/api/blacklist/check.post.ts
+@@ -3,9 +3,39 @@ import { db } from '~/drizzle/db'
+ import { songBlacklists, systemSettings } from '~/drizzle/schema'
+ import { eq } from 'drizzle-orm'
+
++function isCJK(text: string): boolean {
++ return /[\u4e00-\u9fa5]/.test(text)
++}
++function isLatin(text: string): boolean {
++ return /[A-Za-z]/.test(text)
++}
++function isKana(text: string): boolean {
++ return /[\u3040-\u30ff]/.test(text)
++}
++function isHangul(text: string): boolean {
++ return /[\uac00-\ud7af]/.test(text)
++}
++function isCyrillic(text: string): boolean {
++ return /[\u0400-\u04ff]/.test(text)
++}
++function isArabic(text: string): boolean {
++ return /[\u0600-\u06ff]/.test(text)
++}
++function detectLanguages(text: string): string[] {
++ const result: string[] = []
++ if (!text) return result
++ if (isCJK(text)) result.push('zh', '涓枃', 'chinese', 'cjk')
++ if (isLatin(text)) result.push('en', '鑻辨枃', 'english', 'latin')
++ if (isKana(text)) result.push('ja', '鏃ユ枃', 'japanese')
++ if (isHangul(text)) result.push('ko', '闊╂枃', 'korean')
++ if (isCyrillic(text)) result.push('ru', '淇勬枃', 'russian', 'cyrillic')
++ if (isArabic(text)) result.push('ar', '闃挎媺浼枃', 'arabic')
++ return result
++}
++
+ export default defineEventHandler(async (event) => {
+ const body = await readBody(event)
+- const { title, artist } = body
++ const { title, artist, genre } = body
+
+ if (!title) {
+ throw createError({
+@@ -15,24 +45,31 @@ export default defineEventHandler(async (event) => {
+ }
+
+ try {
+- // 鑾峰彇绯荤粺璁剧疆
+ const systemSettingsResult = await db.select().from(systemSettings).limit(1)
+ const systemSettingsData = systemSettingsResult[0]
+ const showBlacklistKeywords = systemSettingsData?.showBlacklistKeywords ?? false
+
+- // 鑾峰彇鎵€鏈夋椿璺冪殑榛戝悕鍗曢」
+ const blacklistItems = await db
+ .select()
+ .from(songBlacklists)
+ .where(eq(songBlacklists.isActive, true))
+
+ const songFullName = `${title} - ${artist || ''}`.toLowerCase()
+- const blocked = []
++ const genreLower = (genre || '').toLowerCase()
++ const combinedText = `${title} ${artist || ''} ${genre || ''}`
++ const textLanguages = new Set([
++ ...detectLanguages(title || ''),
++ ...detectLanguages(artist || ''),
++ ...detectLanguages(genre || '')
++ ])
++
++ const blocked: Array<{ type: string; value: string | null; reason: string }> = []
+
+ for (const item of blacklistItems) {
++ const valueLower = item.value.toLowerCase()
++
+ if (item.type === 'SONG') {
+- // 妫€鏌ュ叿浣撴瓕鏇?- if (songFullName.includes(item.value.toLowerCase())) {
++ if (songFullName.includes(valueLower)) {
+ blocked.push({
+ type: 'song',
+ value: item.value,
+@@ -40,15 +77,38 @@ export default defineEventHandler(async (event) => {
+ })
+ }
+ } else if (item.type === 'KEYWORD') {
+- // 妫€鏌ュ叧閿瘝
+- if (songFullName.includes(item.value.toLowerCase())) {
++ if (songFullName.includes(valueLower) || (genreLower && genreLower.includes(valueLower))) {
+ blocked.push({
+ type: 'keyword',
+- value: showBlacklistKeywords ? item.value : null, // 鏍规嵁璁剧疆鍐冲畾鏄惁鏄剧ず鍏蜂綋鍏抽敭璇?+ value: showBlacklistKeywords ? item.value : null,
+ reason:
+ item.reason || (showBlacklistKeywords ? `鍖呭惈鍏抽敭璇嶏細${item.value}` : '鍖呭惈鍏抽敭璇?)
+ })
+ }
++ } else if (item.type === 'LANGUAGE') {
++ const tokens = [
++ valueLower,
++ ...valueLower.split(/[,锛孿/|\s]+/).map((s) => s.trim()).filter(Boolean)
++ ]
++ const hit = tokens.some((lang) => textLanguages.has(lang))
++ if (hit) {
++ blocked.push({
++ type: 'language',
++ value: showBlacklistKeywords ? item.value : null,
++ reason: item.reason || `璇ヨ绉嶆瓕鏇插凡琚檺鍒讹細${showBlacklistKeywords ? item.value : ''}`.trim()
++ })
++ }
++ } else if (item.type === 'GENRE') {
++ const combinedForGenre = `${genreLower} ${combinedText}`.toLowerCase()
++ const tokens = valueLower.split(/[,锛孿/|\s]+/).map((s) => s.trim()).filter(Boolean)
++ const hit = tokens.some((tok) => combinedForGenre.includes(tok))
++ if (hit) {
++ blocked.push({
++ type: 'genre',
++ value: showBlacklistKeywords ? item.value : null,
++ reason: item.reason || `璇ユ洸椋庢瓕鏇插凡琚檺鍒讹細${showBlacklistKeywords ? item.value : ''}`.trim()
++ })
++ }
+ }
+ }
+
+@@ -57,7 +117,8 @@ export default defineEventHandler(async (event) => {
+ reasons: blocked,
+ song: {
+ title,
+- artist
++ artist,
++ genre: genre || null
+ }
+ }
+ } catch (error) {
+diff --git a/server/api/open/schedules.get.ts b/server/api/open/schedules.get.ts
+index 4139815..9744bd4 100644
+--- a/server/api/open/schedules.get.ts
++++ b/server/api/open/schedules.get.ts
+@@ -1,12 +1,17 @@
+ import { createError, defineEventHandler, getQuery } from 'h3'
+ import { db } from '~/drizzle/db'
+-import { playTimes, schedules, songCollaborators, songs, users } from '~/drizzle/schema'
++import { playTimes, schedules, songCollaborators, songs, systemSettings, users } from '~/drizzle/schema'
+ import { and, asc, desc, eq, gte, inArray, like, lt, or, sql } from 'drizzle-orm'
+ import { formatDateTime } from '~/utils/timeUtils'
+ import { openApiCache } from '~~/server/utils/open-api-cache'
+ import { CACHE_CONSTANTS } from '~~/server/config/constants'
+ import { cacheService } from '~~/server/services/cacheService'
+ import { executeRedisCommand, isRedisReady } from '~~/server/utils/redis'
++import {
++ resolveScheduleVisibilityRange,
++ isPlayDateInRange,
++ type VisibilitySettingsLike
++} from '~~/server/utils/schedule-visibility'
+
+ export default defineEventHandler(async (event) => {
+ try {
+@@ -36,6 +41,27 @@ export default defineEventHandler(async (event) => {
+ const sortBy = (query.sortBy as string) || 'playDate'
+ const sortOrder = (query.sortOrder as string) || 'asc'
+
++ // 璇诲彇绯荤粺璁剧疆锛氭帓鏈熷彲瑙佽寖鍥?+ let visibilityRange = { startAt: null as Date | null, endAt: null as Date | null }
++ try {
++ const systemSettingsDataResult = await db.select().from(systemSettings).limit(1)
++ const systemSettingsData = systemSettingsDataResult[0] || null
++ const visibilitySettings: VisibilitySettingsLike = {
++ scheduleDaysBeforeEnabled: systemSettingsData?.scheduleDaysBeforeEnabled,
++ scheduleDaysBefore: systemSettingsData?.scheduleDaysBefore,
++ scheduleDaysAfterEnabled: systemSettingsData?.scheduleDaysAfterEnabled,
++ scheduleDaysAfter: systemSettingsData?.scheduleDaysAfter
++ }
++ visibilityRange = resolveScheduleVisibilityRange(visibilitySettings)
++ } catch (err) {
++ console.warn('[Schedules API] 璇诲彇鎺掓湡鍙鑼冨洿璁剧疆澶辫触锛屽皢灞曠ず鍏ㄩ儴:', err)
++ }
++
++ const applyVisibilityFilter = (items: any[]): any[] => {
++ if (!visibilityRange.startAt && !visibilityRange.endAt) return items
++ return items.filter((s) => isPlayDateInRange(s.playDate, visibilityRange))
++ }
++
+ // 浼樺厛浠庢櫘閫欰PI缂撳瓨鑾峰彇鏁版嵁
+
+ // 灏濊瘯浠嶤acheService鑾峰彇鎺掓湡鏁版嵁
+@@ -49,6 +75,7 @@ export default defineEventHandler(async (event) => {
+ if (semester) {
+ filteredSchedules = cachedSchedules.filter((s) => s.song?.semester === semester)
+ }
++ filteredSchedules = applyVisibilityFilter(filteredSchedules)
+
+ // 杞崲涓哄紑鏀続PI鏍煎紡
+ const result = {
+@@ -105,11 +132,12 @@ export default defineEventHandler(async (event) => {
+ })
+
+ if (redisData) {
++ const filteredRedisData = applyVisibilityFilter(redisData)
+ // 杞崲涓哄紑鏀続PI鏍煎紡
+ const result = {
+ success: true,
+ data: {
+- schedules: redisData.map((schedule: any) => ({
++ schedules: filteredRedisData.map((schedule: any) => ({
+ id: schedule.id,
+ playDate: schedule.playDate,
+ sequence: schedule.sequence,
+@@ -133,7 +161,7 @@ export default defineEventHandler(async (event) => {
+ }
+ : null
+ })),
+- total: redisData.length,
++ total: filteredRedisData.length,
+ semester: semester || null
+ }
+ }
+@@ -175,6 +203,13 @@ export default defineEventHandler(async (event) => {
+ conditions.push(or(like(songs.title, `%${search}%`)!, like(songs.artist, `%${search}%`)!)!)
+ }
+
++ if (visibilityRange.startAt) {
++ conditions.push(gte(schedules.playDate, visibilityRange.startAt)!)
++ }
++ if (visibilityRange.endAt) {
++ conditions.push(lt(schedules.playDate, visibilityRange.endAt)!)
++ }
++
+ const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
+
+ // 璁$畻鍋忕Щ閲?diff --git a/server/api/roles.get.ts b/server/api/roles.get.ts
+index 415d3d7..7bd2a6c 100644
+--- a/server/api/roles.get.ts
++++ b/server/api/roles.get.ts
+@@ -1,4 +1,4 @@
+-import { ROLES } from '../utils/permissions.js'
++import { ROLES } from '~~/server/utils/permissions'
+
+ export default defineEventHandler(() => {
+ return ROLES
+diff --git a/server/api/site-config.get.ts b/server/api/site-config.get.ts
+index 8ead517..0ad6af9 100644
+--- a/server/api/site-config.get.ts
++++ b/server/api/site-config.get.ts
+@@ -2,6 +2,7 @@ import { db } from '~/drizzle/db'
+ import { systemSettings } from '~/drizzle/schema'
+ import { cacheService } from '../services/cacheService'
+ import { isRedisReady } from '../utils/redis'
++import { SYSTEM_SETTINGS_DEFAULTS } from '~~/server/utils/system-settings-defaults'
+
+ // 杩囨护鏁忔劅瀛楁锛屽彧杩斿洖鍏紑閰嶇疆
+ const publicFields = [
+@@ -26,17 +27,23 @@ const publicFields = [
+ 'enableSubmissionRemarks',
+ 'enableRequestTimeLimitation',
+ 'forceBlockAllRequests',
+- 'smtpEnabled'
++ 'smtpEnabled',
++ 'defaultTheme',
++ 'enabledThemes',
++ 'scheduleDaysBeforeEnabled',
++ 'scheduleDaysBefore',
++ 'scheduleDaysAfterEnabled',
++ 'scheduleDaysAfter',
++ 'statisticsCode',
++ 'statisticsCodeEnabled'
+ ]
+
+ const filterPublicSettings = (data: any) => {
+- if (!data) {
+- return {}
+- }
++ const merged: any = { ...SYSTEM_SETTINGS_DEFAULTS, ...(data || {}) }
+ const result: Record = {}
+ for (const key of publicFields) {
+- if (Object.prototype.hasOwnProperty.call(data, key)) {
+- result[key] = data[key]
++ if (Object.prototype.hasOwnProperty.call(merged, key)) {
++ result[key] = merged[key]
+ }
+ }
+ return result
+@@ -49,7 +56,7 @@ export default defineEventHandler(async (event) => {
+ const cachedSettings = await cacheService.getSystemSettings()
+ if (cachedSettings) {
+ console.log('[API] 绯荤粺璁剧疆缂撳瓨鍛戒腑')
+- return filterPublicSettings(cachedSettings)
++ return filterPublicSettings({ ...SYSTEM_SETTINGS_DEFAULTS, ...cachedSettings })
+ }
+ }
+
+@@ -70,7 +77,8 @@ export default defineEventHandler(async (event) => {
+ siteDescription: '鏍″洯骞挎挱绔欑偣姝岀郴缁?- 璁╀綘鐨勫0闊宠鍚',
+ siteVersion: process.env.NUXT_PUBLIC_SITE_VERSION || null,
+ submissionGuidelines: '璇烽伒瀹堟牎鍥瀹氾紝鎻愪氦鍋ュ悍鍚戜笂鐨勬瓕鏇层€?,
+- icpNumber: null,
++ icpNumber: '绮CP澶?025450093鍙?,
++ gonganNumber: null,
+ enableSubmissionLimit: false,
+ dailySubmissionLimit: null,
+ weeklySubmissionLimit: null,
+@@ -81,14 +89,20 @@ export default defineEventHandler(async (event) => {
+ enableRequestTimeLimitation: false,
+ forceBlockAllRequests: false,
+ enableReplayRequests: false,
+- smtpEnabled: false
++ smtpEnabled: false,
++ defaultTheme: 'ClassicLight',
++ enabledThemes: '["ClassicLight","ModernLight"]',
++ scheduleDaysBeforeEnabled: false,
++ scheduleDaysBefore: 30,
++ scheduleDaysAfterEnabled: false,
++ scheduleDaysAfter: 14
+ })
+ .returning()
+
+ settings = newSettings[0]
+ }
+
+- const publicSettings = filterPublicSettings(settings)
++ const publicSettings = filterPublicSettings({ ...SYSTEM_SETTINGS_DEFAULTS, ...(settings || {}) })
+
+ // 灏嗙粨鏋滅紦瀛樺埌Redis锛堝鏋滃彲鐢級- 姘镐箙缂撳瓨
+ if (settings && isRedisReady()) {
+diff --git a/server/api/songs/[id]/update.put.ts b/server/api/songs/[id]/update.put.ts
+index abe3c9c..3d5b70d 100644
+--- a/server/api/songs/[id]/update.put.ts
++++ b/server/api/songs/[id]/update.put.ts
+@@ -1,8 +1,16 @@
+ import { db } from '~/drizzle/db'
+-import { songs, users, songCollaborators, collaborationLogs } from '~/drizzle/schema'
+-import { eq, or } from 'drizzle-orm'
++import {
++ songs,
++ users,
++ songCollaborators,
++ collaborationLogs,
++ systemSettings,
++ songBlacklists
++} from '~/drizzle/schema'
++import { and, eq, inArray, or } from 'drizzle-orm'
+ import { cacheService } from '~~/server/services/cacheService'
+ import { createSubmissionNoteClearedNotification } from '~~/server/services/notificationService'
++import { moderateSubmissionNote } from '~~/server/utils/submissionNoteAutoModeration'
+
+ export default defineEventHandler(async (event) => {
+ try {
+@@ -122,6 +130,38 @@ export default defineEventHandler(async (event) => {
+ ? body.submissionNote.trim()
+ : null
+ }
++ if ('submissionNotePublic' in body) {
++ const wantPublic = updateData.submissionNote ? body.submissionNotePublic !== false : false
++ updateData.submissionNotePublic = wantPublic
++
++ // 鑷姩瀹℃煡锛堣嫢 systemSettings 鎵撳紑 submissionNoteRequiresApproval锛?+ try {
++ const sysSetRes = await db.select().from(systemSettings).limit(1)
++ const sysSet = sysSetRes[0]
++ if (sysSet?.submissionNoteRequiresApproval && wantPublic && updateData.submissionNote) {
++ const bl = await db
++ .select()
++ .from(songBlacklists)
++ .where(inArray(songBlacklists.type, ['KEYWORD', 'LANGUAGE', 'GENRE']))
++ const mod = moderateSubmissionNote(
++ updateData.submissionNote,
++ wantPublic,
++ true,
++ {
++ title: updateData.title || existingSong.title,
++ artist: updateData.artist || existingSong.artist
++ },
++ bl
++ )
++ updateData.submissionNotePublic = mod.notePublic
++ updateData.submissionNotePublicStatus = mod.publicStatus
++ } else {
++ updateData.submissionNotePublicStatus = null
++ }
++ } catch (err) {
++ console.warn('[Update API] 鎶曠鐣欒█鑷姩瀹℃煡澶辫触锛屼繚鎸佺敤鎴疯缃?', err)
++ }
++ }
+
+ const currentRequesterId = updateData.requesterId || existingSong.requesterId
+
+diff --git a/server/api/songs/add.post.ts b/server/api/songs/add.post.ts
+index ee7850c..b625176 100644
+--- a/server/api/songs/add.post.ts
++++ b/server/api/songs/add.post.ts
+@@ -2,6 +2,7 @@ import { db } from '~/drizzle/db'
+ import { songs, users } from '~/drizzle/schema'
+ import { and, eq, or } from 'drizzle-orm'
+ import { cacheService } from '~~/server/services/cacheService'
++import { normalizeCoverUrl } from '~~/server/utils/url'
+
+ export default defineEventHandler(async (event) => {
+ try {
+@@ -87,6 +88,8 @@ export default defineEventHandler(async (event) => {
+ })
+ }
+
++ const normalizedCover = normalizeCoverUrl(cover, `add-song:${title || ''}`)
++
+ // 鍒涘缓姝屾洸
+ const newSongResult = await db
+ .insert(songs)
+@@ -99,8 +102,8 @@ export default defineEventHandler(async (event) => {
+ musicPlatform: musicPlatform || null,
+ musicId: musicId || null,
+ kgHash: musicPlatform === 'kugou' ? musicId : null,
+- cover: cover || null,
+- coverUrl: cover || null,
++ cover: normalizedCover,
++ coverUrl: normalizedCover,
+ playUrl: playUrl || null
+ })
+ .returning()
+diff --git a/server/api/songs/import.post.ts b/server/api/songs/import.post.ts
+index fb48f03..6224ace 100644
+--- a/server/api/songs/import.post.ts
++++ b/server/api/songs/import.post.ts
+@@ -2,6 +2,7 @@ import { defineEventHandler, createError, readBody } from 'h3'
+ import { db } from '~/drizzle/db'
+ import { songs, semesters, songBlacklists } from '~/drizzle/schema'
+ import { eq, inArray, and, asc } from 'drizzle-orm'
++import { normalizeCoverUrl } from '~~/server/utils/url'
+
+ export default defineEventHandler(async (event) => {
+ const user = event.context.user
+@@ -120,6 +121,8 @@ export default defineEventHandler(async (event) => {
+ continue
+ }
+
++ const normalizedCover = normalizeCoverUrl(song.cover, `import-song:${song.title || ''}`)
++
+ // 3. 鍑嗗鎻掑叆鏁版嵁
+ songsToInsert.push({
+ title: song.title,
+@@ -129,7 +132,8 @@ export default defineEventHandler(async (event) => {
+ playedAt: null,
+ semester: currentSemesterName,
+ preferredPlayTimeId: null, // 閲嶇疆鍋忓ソ鏃堕棿
+- cover: song.cover,
++ cover: normalizedCover,
++ coverUrl: normalizedCover,
+ playUrl: song.playUrl,
+ musicPlatform: song.musicPlatform,
+ musicId: song.musicId,
+diff --git a/server/api/songs/index.get.ts b/server/api/songs/index.get.ts
+index cb817f6..a8274b4 100644
+--- a/server/api/songs/index.get.ts
++++ b/server/api/songs/index.get.ts
+@@ -8,9 +8,10 @@ import {
+ songs,
+ systemSettings,
+ users,
+- votes
++ votes,
++ scheduleSongPool
+ } from '~/drizzle/schema'
+-import { and, asc, count, desc, eq, inArray, like, or } from 'drizzle-orm'
++import { and, asc, count, desc, eq, inArray, like, or, isNull, notInArray } from 'drizzle-orm'
+ import { cacheService } from '~~/server/services/cacheService'
+ import { formatDateTime } from '~/utils/timeUtils'
+ import { maskSongsInfo, MaskableSong, MaskableUser } from '~~/server/utils/studentMask'
+@@ -67,6 +68,7 @@ export default defineEventHandler(async (event) => {
+ const sortBy = (query.sortBy as string) || 'createdAt'
+ const sortOrder = (query.sortOrder as string) || 'desc'
+ const bypassCache = query.bypass_cache === 'true'
++ const usePool = query.use_pool === 'true' || query.usePool === 'true'
+
+ // 鑾峰彇鐢ㄦ埛韬唤
+ const user = event.context.user || null
+@@ -203,6 +205,35 @@ export default defineEventHandler(async (event) => {
+ conditions.push(eq(songs.requesterId, user.id))
+ }
+
++ if (usePool) {
++ const poolRows = await db
++ .select({ songId: scheduleSongPool.songId })
++ .from(scheduleSongPool)
++ .where(
++ and(
++ eq(scheduleSongPool.status, 'PENDING'),
++ isNull(scheduleSongPool.targetPlayDate)
++ )
++ )
++ .groupBy(scheduleSongPool.songId)
++
++ const allScheduledSongIdsQuery = await db
++ .select({ songId: schedules.songId })
++ .from(schedules)
++
++ const scheduledIdsSet = new Set(allScheduledSongIdsQuery.map((r) => r.songId))
++ const candidateFromPool = new Set(
++ poolRows.map((r) => r.songId).filter((id) => !scheduledIdsSet.has(id))
++ )
++
++ if (candidateFromPool.size === 0) {
++ conditions.push(eq(songs.id, -1))
++ } else {
++ conditions.push(inArray(songs.id, Array.from(candidateFromPool)))
++ }
++ conditions.push(eq(songs.played, false))
++ }
++
+ const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
+
+ // 鏌ヨ姝屾洸鎬绘暟
+diff --git a/server/api/songs/public.get.ts b/server/api/songs/public.get.ts
+index 6cd730c..d342e44 100644
+--- a/server/api/songs/public.get.ts
++++ b/server/api/songs/public.get.ts
+@@ -11,7 +11,12 @@ import {
+ users,
+ votes
+ } from '~/drizzle/schema'
+-import { and, count, desc, eq, inArray } from 'drizzle-orm'
++import { and, count, desc, eq, gte, inArray, lt, like, or } from 'drizzle-orm'
++import {
++ resolveScheduleVisibilityRange,
++ isPlayDateInRange,
++ type VisibilitySettingsLike
++} from '~~/server/utils/schedule-visibility'
+ import { cacheService } from '~~/server/services/cacheService'
+ import { executeRedisCommand, isRedisReady } from '../../utils/redis'
+ import { formatDateTime } from '~/utils/timeUtils'
+@@ -38,6 +43,13 @@ export default defineEventHandler(async (event) => {
+ .limit(1)
+ const systemSettingsData = systemSettingsDataResult[0] || null
+ const shouldHideStudentInfo = systemSettingsData?.hideStudentInfo ?? true
++ const visibilitySettings: VisibilitySettingsLike = {
++ scheduleDaysBeforeEnabled: systemSettingsData?.scheduleDaysBeforeEnabled,
++ scheduleDaysBefore: systemSettingsData?.scheduleDaysBefore,
++ scheduleDaysAfterEnabled: systemSettingsData?.scheduleDaysAfterEnabled,
++ scheduleDaysAfter: systemSettingsData?.scheduleDaysAfter
++ }
++ const visibilityRange = resolveScheduleVisibilityRange(visibilitySettings)
+
+ // 鍒濆鍖栫紦瀛樻湇鍔?
+@@ -73,7 +85,14 @@ export default defineEventHandler(async (event) => {
+ })
+
+ if (cachedData) {
+- return cachedData
++ const filtered = visibilityRange.startAt || visibilityRange.endAt
++ ? cachedData.filter((s) => isPlayDateInRange(s.playDate, visibilityRange))
++ : cachedData
++ const resultData = JSON.parse(JSON.stringify(filtered)) as PublicScheduleItem[]
++ if (shouldHideStudentInfo && !isAdmin) {
++ maskPublicScheduleData(resultData)
++ }
++ return resultData
+ }
+ }
+
+@@ -91,6 +110,12 @@ export default defineEventHandler(async (event) => {
+ filteredSchedules = cachedSchedules.filter((s) => s.song?.semester === semester)
+ console.log(`[Cache] 杩囨护瀛︽湡 ${semester} 鍚庣殑鏁伴噺: ${filteredSchedules.length}`)
+ }
++ // 搴旂敤鎺掓湡鍙鑼冨洿杩囨护
++ if (visibilityRange.startAt || visibilityRange.endAt) {
++ filteredSchedules = filteredSchedules.filter((s) =>
++ isPlayDateInRange(s.playDate, visibilityRange)
++ )
++ }
+
+ // 灏嗚繃婊ゅ悗鐨勬暟鎹紦瀛樺埌Redis锛堢紦瀛樺畬鏁存暟鎹紝涓嶈繘琛屾ā绯婂寲锛? if (isRedisReady()) {
+@@ -116,6 +141,13 @@ export default defineEventHandler(async (event) => {
+ }
+
+ // 鑾峰彇鎺掓湡鐨勬瓕鏇诧紝鍖呭惈鎾斁鏃舵淇℃伅锛堟煡璇㈡墍鏈夊鏈熺殑鏁版嵁锛?+ const scheduleWhere: any[] = [eq(schedules.isDraft, false)]
++ if (visibilityRange.startAt) {
++ scheduleWhere.push(gte(schedules.playDate, visibilityRange.startAt)!)
++ }
++ if (visibilityRange.endAt) {
++ scheduleWhere.push(lt(schedules.playDate, visibilityRange.endAt)!)
++ }
+ const schedulesData = await db
+ .select({
+ id: schedules.id,
+@@ -153,10 +185,9 @@ export default defineEventHandler(async (event) => {
+ .leftJoin(songs, eq(schedules.songId, songs.id))
+ .leftJoin(users, eq(songs.requesterId, users.id))
+ .leftJoin(playTimes, eq(schedules.playTimeId, playTimes.id))
+- .where(eq(schedules.isDraft, false)) // 鍙煡璇㈠凡鍙戝竷鐨勬帓鏈?+ .where(and(...scheduleWhere)) // 鍙煡璇㈠凡鍙戝竷鐨勬帓鏈燂紙+鍙鑼冨洿锛? .orderBy(schedules.playDate)
+
+- // 鑾峰彇姣忛姝岀殑鎶曠エ鏁? const voteCountsQuery = await db
+ .select({
+ songId: votes.songId,
+diff --git a/server/api/songs/request.post.ts b/server/api/songs/request.post.ts
+index 632859a..22f9761 100644
+--- a/server/api/songs/request.post.ts
++++ b/server/api/songs/request.post.ts
+@@ -4,14 +4,17 @@ import {
+ playTimes,
+ requestTimes,
+ semesters,
++ songBlacklists,
+ songCollaborators,
+ songs,
+ systemSettings
+ } from '~/drizzle/db'
+-import { and, eq, gt, gte, lt, lte, sql } from 'drizzle-orm'
++import { and, eq, gt, gte, inArray, lt, lte, sql } from 'drizzle-orm'
+ import { createCollaborationInvitationNotification } from '~~/server/services/notificationService'
+ import { isLimitReached } from '~~/server/utils/submissionLimit'
+ import { getBeijingTimeISOString } from '~/utils/timeUtils'
++import { moderateSubmissionNote } from '~~/server/utils/submissionNoteAutoModeration'
++import { normalizeCoverUrl } from '~~/server/utils/url'
+
+ export default defineEventHandler(async (event) => {
+ // 妫€鏌ョ敤鎴疯璇?@@ -132,6 +135,18 @@ export default defineEventHandler(async (event) => {
+ const systemSettingsData = systemSettingsResult[0]
+ const isAdmin = user.role === 'SUPER_ADMIN' || user.role === 'ADMIN'
+
++ // 鍔犺浇鑷姩瀹℃煡闇€瑕佺殑榛戝悕鍗曪紙KEYWORD / LANGUAGE / GENRE锛?+ let moderationBlacklists: any[] = []
++ try {
++ moderationBlacklists = await db
++ .select()
++ .from(songBlacklists)
++ .where(inArray(songBlacklists.type, ['KEYWORD', 'LANGUAGE', 'GENRE']))
++ } catch (err) {
++ console.warn('[Request API] 鍔犺浇鑷姩瀹℃煡榛戝悕鍗曞け璐ワ紝灏嗚烦杩囪嚜鍔ㄥ鏌?', err)
++ moderationBlacklists = []
++ }
++
+ // 1. 妫€鏌ュ己鍒跺叧闂? if (systemSettingsData?.forceBlockAllRequests && !isAdmin) {
+ throw createError({
+@@ -243,8 +258,34 @@ export default defineEventHandler(async (event) => {
+ }
+ const submissionNote =
+ systemSettingsData?.enableSubmissionRemarks && rawSubmissionNote ? rawSubmissionNote : null
+- const submissionNotePublic =
+- submissionNote !== null ? body.submissionNotePublic !== false : false
++ const rawSubmissionNotePublic = submissionNote !== null ? body.submissionNotePublic !== false : false
++ const requiresApproval = Boolean(systemSettingsData?.submissionNoteRequiresApproval)
++
++ let finalSubmissionNotePublic = rawSubmissionNotePublic
++ let submissionNotePublicStatus: string | null = null
++
++ if (submissionNote && rawSubmissionNotePublic && requiresApproval) {
++ const mod = moderateSubmissionNote(
++ submissionNote,
++ rawSubmissionNotePublic,
++ requiresApproval,
++ { title: body.title, artist: body.artist, genre: body.genre || null },
++ moderationBlacklists
++ )
++ finalSubmissionNotePublic = mod.notePublic
++ submissionNotePublicStatus = mod.publicStatus
++ if (mod.rejectedReasons.length > 0) {
++ console.log(
++ `[Request API] 鎶曠鐣欒█鑷姩瀹℃煡鏈€氳繃锛屾瓕鏇?${body.title} - ${body.artist}锛屽師鍥?`,
++ mod.rejectedReasons
++ )
++ }
++ } else if (submissionNote) {
++ // 娌″紑鑷姩瀹℃煡锛屼篃璁板綍涓€涓嬬姸鎬?+ submissionNotePublicStatus = null
++ }
++
++ const submissionNotePublic = finalSubmissionNotePublic
+
+ // 鍑嗗鍙戦€侀€氱煡鐨勫垪琛? const notificationsToSend: { userId: number; songId: number; songTitle: string }[] = []
+@@ -350,6 +391,8 @@ export default defineEventHandler(async (event) => {
+ }
+ }
+
++ const normalizedCover = normalizeCoverUrl(body.cover, `request-song:${body.title || ''}`)
++
+ const songResult = await tx
+ .insert(songs)
+ .values({
+@@ -359,14 +402,15 @@ export default defineEventHandler(async (event) => {
+ requesterId: user.id,
+ preferredPlayTimeId: preferredPlayTime?.id || null,
+ semester: currentSemester, // 浣跨敤澶栭儴鑾峰彇鐨勫鏈熷悕绉?- cover: body.cover || null,
+- coverUrl: body.cover || null,
++ cover: normalizedCover,
++ coverUrl: normalizedCover,
+ musicPlatform: isBilibili ? 'bilibili' : (body.musicPlatform || null),
+ musicId: finalMusicId,
+ kgHash: body.musicPlatform === 'kugou' ? body.musicId : null,
+ playUrl: body.playUrl || null,
+ submissionNote,
+ submissionNotePublic,
++ submissionNotePublicStatus,
+ hitRequestId: hitRequestTime?.id || null,
+ expectedPlayDate: body.expectedPlayDate || null,
+ expectedPlaySequence: body.expectedPlaySequence !== undefined ? Number(body.expectedPlaySequence) : null
+diff --git a/server/config/constants.ts b/server/config/constants.ts
+index daa4c29..f708722 100644
+--- a/server/config/constants.ts
++++ b/server/config/constants.ts
+@@ -180,3 +180,13 @@ export const RISK_CONTROL_CONSTANTS = {
+ SONG_IP_DOMINANCE_PERCENT: 0.6,
+ SONG_IP_MIN_SAMPLE: 8
+ } as const
++
++export const SERVER_ERROR_CODES = {
++ AUTH_UNAUTHORIZED: 'AUTH_UNAUTHORIZED',
++ AUTH_FORBIDDEN: 'AUTH_FORBIDDEN',
++ SONG_NOT_FOUND: 'SONG_NOT_FOUND',
++ SONG_OPERATION_FAILED: 'SONG_OPERATION_FAILED',
++ SONG_BATCH_REJECT_IDS_REQUIRED: 'SONG_BATCH_REJECT_IDS_REQUIRED',
++ SONG_BATCH_REJECT_LIMIT_EXCEEDED: 'SONG_BATCH_REJECT_LIMIT_EXCEEDED',
++ SONG_BATCH_REJECT_REASON_REQUIRED: 'SONG_BATCH_REJECT_REASON_REQUIRED'
++} as const
+diff --git a/server/plugins/sentry.ts b/server/plugins/sentry.ts
+new file mode 100644
+index 0000000..daec8c9
+--- /dev/null
++++ b/server/plugins/sentry.ts
+@@ -0,0 +1,24 @@
++import * as Sentry from '@sentry/node'
++
++export default defineNitroPlugin((nitroApp) => {
++ const config = useRuntimeConfig()
++ const dsn = config.sentry?.serverDsn || config.sentry?.dsn
++
++ if (!dsn) return
++
++ Sentry.init({
++ dsn,
++ environment: config.sentry?.environment || process.env.NODE_ENV || 'production',
++ release: config.sentry?.release || undefined,
++ tracesSampleRate: config.sentry?.errorSampleRate ?? 1.0,
++ sampleRate: config.sentry?.errorSampleRate ?? 1.0
++ })
++
++ nitroApp.hooks.hook('error', (error) => {
++ Sentry.captureException(error)
++ })
++
++ nitroApp.hooks.hook('request', (event) => {
++ event.context.sentry = Sentry
++ })
++})
+diff --git a/server/services/cacheService.ts b/server/services/cacheService.ts
+index 7c91700..c28cbad 100644
+--- a/server/services/cacheService.ts
++++ b/server/services/cacheService.ts
+@@ -16,7 +16,7 @@ const CACHE_TTL = {
+ SONG_COUNT: 180, // 3鍒嗛挓
+ USER_VOTES: 120, // 2鍒嗛挓
+ EMPTY_RESULT: 60, // 1鍒嗛挓锛堢┖缁撴灉缂撳瓨鏃堕棿杈冪煭锛?- SYSTEM_SETTINGS: 3600, // 60鍒嗛挓
++ SYSTEM_SETTINGS: 900, // 15鍒嗛挓锛堟柊鍔熻兘涓婄嚎鍚庨伩鍏?Redis 缂撳瓨鏃?DB 鍊煎鑷翠富棰?鎺掓湡绛夐厤缃け鏁堬級
+ RANDOM_OFFSET: 60, // 闅忔満鍋忕Щ閲忥紝闃叉缂撳瓨闆穿
+ LOCK_TIMEOUT: 30, // 鍒嗗竷寮忛攣瓒呮椂鏃堕棿
+ REFRESH_AHEAD: 60 // 鎻愬墠鍒锋柊鏃堕棿锛堢锛?diff --git a/server/services/cardCodeDeleteService.ts b/server/services/cardCodeDeleteService.ts
+new file mode 100644
+index 0000000..eecbfaf
+--- /dev/null
++++ b/server/services/cardCodeDeleteService.ts
+@@ -0,0 +1,20 @@
++import { inArray } from 'drizzle-orm'
++import { db } from '~/drizzle/db'
++import { cardCodes, cardCodeRedemptions, songs } from '~/drizzle/schema'
++
++export async function deleteCardCodesByIds(ids: number[]) {
++ return await db.transaction(async (tx) => {
++ await tx.update(songs)
++ .set({ cardCodeId: null })
++ .where(inArray(songs.cardCodeId, ids))
++
++ await tx.delete(cardCodeRedemptions)
++ .where(inArray(cardCodeRedemptions.cardCodeId, ids))
++
++ const deleted = await tx.delete(cardCodes)
++ .where(inArray(cardCodes.id, ids))
++ .returning()
++
++ return deleted
++ })
++}
+diff --git a/server/utils/admin-password-policy.ts b/server/utils/admin-password-policy.ts
+new file mode 100644
+index 0000000..9755036
+--- /dev/null
++++ b/server/utils/admin-password-policy.ts
+@@ -0,0 +1,35 @@
++import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH } from '~/utils/password-policy'
++
++interface AdminPasswordViolation {
++ code: string
++ message: string
++}
++
++export function getAdminPasswordViolation(password: unknown): AdminPasswordViolation | null {
++ if (typeof password !== 'string' || !password) {
++ return { code: 'AUTH_NEW_PASSWORD_REQUIRED', message: '鏂板瘑鐮佷笉鑳戒负绌? }
++ }
++
++ if (password.length < PASSWORD_MIN_LENGTH) {
++ return {
++ code: 'AUTH_PASSWORD_TOO_SHORT',
++ message: `瀵嗙爜闀垮害涓嶈兘灏戜簬${PASSWORD_MIN_LENGTH}涓瓧绗
++ }
++ }
++
++ if (password.length > PASSWORD_MAX_LENGTH) {
++ return {
++ code: 'AUTH_PASSWORD_TOO_LONG',
++ message: `瀵嗙爜闀垮害涓嶈兘瓒呰繃${PASSWORD_MAX_LENGTH}浣峘
++ }
++ }
++
++ if (new TextEncoder().encode(password).length > 72) {
++ return {
++ code: 'AUTH_PASSWORD_TOO_MANY_BYTES',
++ message: '瀵嗙爜鏈夋晥闀垮害涓嶈兘瓒呰繃72瀛楄妭'
++ }
++ }
++
++ return null
++}
+diff --git a/server/utils/apiError.ts b/server/utils/apiError.ts
+new file mode 100644
+index 0000000..cea7d95
+--- /dev/null
++++ b/server/utils/apiError.ts
+@@ -0,0 +1,15 @@
++import { createError } from 'h3'
++
++export function createApiError(
++ statusCode: number,
++ code: string,
++ message: string,
++ data?: Record
++) {
++ return createError({
++ statusCode,
++ statusMessage: code,
++ message,
++ data: { code, ...(data || {}) }
++ })
++}
+diff --git a/server/utils/auth-route-policy.ts b/server/utils/auth-route-policy.ts
+new file mode 100644
+index 0000000..b0ef35b
+--- /dev/null
++++ b/server/utils/auth-route-policy.ts
+@@ -0,0 +1,99 @@
++type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
++
++interface RoutePolicy {
++ path: string
++ methods: readonly HttpMethod[]
++}
++
++const GET_METHODS = ['GET', 'HEAD'] as const
++const WRITE_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'] as const
++
++export const PASSWORD_CHANGE_ALLOWED_ROUTES: readonly RoutePolicy[] = [
++ { path: '/api/auth/verify', methods: GET_METHODS },
++ { path: '/api/auth/logout', methods: ['POST'] },
++ { path: '/api/auth/change-password', methods: ['POST'] },
++ { path: '/api/auth/set-initial-password', methods: ['POST'] },
++ { path: '/api/auth/2fa/verify', methods: ['POST'] },
++ { path: '/api/auth/2fa/send-email', methods: ['POST'] },
++ { path: '/api/site-config', methods: GET_METHODS }
++]
++
++export const PUBLIC_API_EXACT_ROUTES: readonly RoutePolicy[] = [
++ { path: '/api/auth/login', methods: ['POST'] },
++ { path: '/api/auth/register', methods: ['POST'] },
++ { path: '/api/auth/email-code', methods: ['POST'] },
++ { path: '/api/auth/grade-class-options', methods: GET_METHODS },
++ { path: '/api/auth/oauth-register-options', methods: GET_METHODS },
++ { path: '/api/auth/bind', methods: ['POST'] },
++ { path: '/api/auth/oauth-register', methods: ['POST'] },
++ { path: '/api/auth/2fa/verify', methods: ['POST'] },
++ { path: '/api/auth/2fa/send-email', methods: ['POST'] },
++ { path: '/api/auth/forgot-password', methods: ['POST'] },
++ { path: '/api/auth/reset-password', methods: ['POST'] },
++ { path: '/api/auth/captcha', methods: GET_METHODS },
++ { path: '/api/semesters/current', methods: GET_METHODS },
++ { path: '/api/play-times', methods: GET_METHODS },
++ { path: '/api/schedules/public', methods: GET_METHODS },
++ { path: '/api/songs/count', methods: GET_METHODS },
++ { path: '/api/songs/public', methods: GET_METHODS },
++ { path: '/api/site-config', methods: GET_METHODS },
++ { path: '/api/system/location', methods: GET_METHODS },
++ { path: '/api/auth/webauthn/login', methods: ['POST'] },
++ { path: '/api/music/resolve-url', methods: ['POST'] },
++ { path: '/api/music/state', methods: ['POST'] },
++ { path: '/api/music/websocket', methods: GET_METHODS },
++ { path: '/api/sys/time', methods: GET_METHODS }
++]
++
++export const PUBLIC_API_PREFIX_ROUTES: readonly RoutePolicy[] = [
++ { path: '/api/proxy/', methods: GET_METHODS },
++ { path: '/api/api-enhanced/', methods: [...GET_METHODS, ...WRITE_METHODS] },
++ { path: '/api/native-api/', methods: [...GET_METHODS, 'POST'] },
++ { path: '/api/open/', methods: [...GET_METHODS, ...WRITE_METHODS] },
++ { path: '/api/auth/webauthn/login/', methods: ['POST'] }
++]
++
++function matchesMethod(policy: RoutePolicy, method: string): boolean {
++ return policy.methods.includes(method.toUpperCase() as HttpMethod)
++}
++
++export function isPublicApiPath(pathname: string, method: string): boolean {
++ return (
++ PUBLIC_API_EXACT_ROUTES.some(
++ (policy) => pathname === policy.path && matchesMethod(policy, method)
++ ) ||
++ PUBLIC_API_PREFIX_ROUTES.some(
++ (policy) => pathname.startsWith(policy.path) && matchesMethod(policy, method)
++ )
++ )
++}
++
++export function isAllowedDuringPasswordChange(pathname: string, method: string): boolean {
++ return PASSWORD_CHANGE_ALLOWED_ROUTES.some(
++ (policy) => pathname === policy.path && matchesMethod(policy, method)
++ )
++}
++
++export function shouldBlockDuringPasswordChange(
++ pathname: string,
++ method: string,
++ requirePasswordChange: boolean
++): boolean {
++ return (
++ requirePasswordChange &&
++ !isPublicApiPath(pathname, method) &&
++ !isAllowedDuringPasswordChange(pathname, method)
++ )
++}
++
++export function shouldBypassPublicApiAuthentication(
++ pathname: string,
++ method: string,
++ hasToken: boolean
++): boolean {
++ return isPublicApiPath(pathname, method) && !hasToken
++}
++
++export function canBindOAuthIdentity(requirePasswordChange: boolean): boolean {
++ return !requirePasswordChange
++}
+diff --git a/server/utils/card-code-delete-handler.ts b/server/utils/card-code-delete-handler.ts
+new file mode 100644
+index 0000000..682954d
+--- /dev/null
++++ b/server/utils/card-code-delete-handler.ts
+@@ -0,0 +1,68 @@
++import { createError, getQuery, readBody } from 'h3'
++import { z } from 'zod'
++import { deleteCardCodesByIds } from '~~/server/services/cardCodeDeleteService'
++
++const MAX_DELETE_COUNT = 500
++const idSchema = z.preprocess(
++ (value) => (typeof value === 'number' || typeof value === 'string' ? value : undefined),
++ z.coerce.number().int().positive()
++)
++
++const deleteCardCodesSchema = z.object({
++ id: idSchema.optional(),
++ ids: z.preprocess(
++ (value) => {
++ if (typeof value === 'string') {
++ return value.split(',').map((item) => item.trim()).filter(Boolean)
++ }
++ return value
++ },
++ z.array(idSchema).max(MAX_DELETE_COUNT, `鍗曟鏈€澶氬垹闄?${MAX_DELETE_COUNT} 涓偣姝屽埜`).optional()
++ )
++})
++
++export const handleOpenCardCodeDelete = async (event: any) => {
++ const apiKey = event.context.apiKey
++ if (!apiKey) {
++ throw createError({ statusCode: 401, message: 'API璁よ瘉澶辫触' })
++ }
++
++ try {
++ const query = getQuery(event)
++ const body = await readBody(event).catch(() => ({})) ?? {}
++ const validatedData = deleteCardCodesSchema.parse({ ...query, ...body })
++ const rawIds = [
++ ...(validatedData.id !== undefined ? [validatedData.id] : []),
++ ...(validatedData.ids || [])
++ ]
++ const ids = Array.from(new Set(rawIds))
++
++ if (!ids.length) {
++ throw createError({ statusCode: 400, message: '缂哄皯鏈夋晥鐐规瓕鍒窱D' })
++ }
++ if (ids.length > MAX_DELETE_COUNT) {
++ throw createError({ statusCode: 400, message: `鍗曟鏈€澶氬垹闄?${MAX_DELETE_COUNT} 涓偣姝屽埜` })
++ }
++
++ const deletedRows = await deleteCardCodesByIds(ids)
++ return {
++ success: true,
++ message: '鐐规瓕鍒稿垹闄ゆ垚鍔?,
++ data: {
++ cardCodes: deletedRows,
++ deleted: deletedRows.length,
++ requested: ids.length
++ }
++ }
++ } catch (err: any) {
++ if (err.statusCode) throw err
++ if (err.name === 'ZodError') {
++ throw createError({
++ statusCode: 400,
++ message: `璇锋眰鍙傛暟楠岃瘉澶辫触锛?{(err.issues || err.errors).map((issue: any) => issue.message).join(', ')}`
++ })
++ }
++ console.error('寮€鏀続PI鍒犻櫎鐐规瓕鍒稿け璐?, err)
++ throw createError({ statusCode: 500, message: '鍒犻櫎鐐规瓕鍒稿け璐? })
++ }
++}
+diff --git a/server/utils/create-auth-session.ts b/server/utils/create-auth-session.ts
+new file mode 100644
+index 0000000..bd45df1
+--- /dev/null
++++ b/server/utils/create-auth-session.ts
+@@ -0,0 +1,83 @@
++import { getHeader } from 'h3'
++import { db, eq, and } from '~/drizzle/db'
++import { authSessions } from '~/drizzle/schema'
++import { parseUserAgent, type LoginMethod } from './user-agent-info'
++import { getClientIP } from './ip-utils'
++import { randomUUID } from 'node:crypto'
++
++export interface CreateAuthSessionOptions {
++ userId: number
++ jti: string
++ loginMethod: LoginMethod
++ expiresAt?: Date
++ oauthProvider?: string
++}
++
++const DEFAULT_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000
++
++export async function createAuthSession(
++ event: any,
++ options: CreateAuthSessionOptions
++): Promise {
++ const { userId, jti, loginMethod, oauthProvider } = options
++ const ua = getHeader(event, 'user-agent')
++ const { browser, operatingSystem, device } = parseUserAgent(ua)
++ const ip = getClientIP(event)
++ const now = new Date()
++ const expiresAt = options.expiresAt ?? new Date(now.getTime() + DEFAULT_SESSION_TTL_MS)
++
++ try {
++ let method = loginMethod
++ if (oauthProvider && method === 'oauth') {
++ method = `oauth:${oauthProvider}` as any
++ }
++
++ await db.insert(authSessions).values({
++ id: randomUUID(),
++ userId,
++ sessionToken: jti,
++ browser,
++ operatingSystem,
++ device,
++ loginMethod: method,
++ ipAddress: ip,
++ userAgent: ua ?? null,
++ createdAt: now,
++ lastActiveAt: now,
++ expiresAt,
++ isRevoked: false
++ })
++ } catch (error) {
++ console.warn('[AuthSession] 鍒涘缓浼氳瘽澶辫触锛岀户缁棤浼氳瘽鐧诲綍:', error)
++ }
++
++ return jti
++}
++
++export async function isSessionRevokedByJti(jti: string | undefined): Promise {
++ if (!jti) return false
++ try {
++ const row = await db.query.authSessions.findFirst({
++ where: and(eq(authSessions.sessionToken, jti)),
++ columns: { isRevoked: true, expiresAt: true }
++ })
++ if (!row) return false
++ if (row.expiresAt && row.expiresAt.getTime() < Date.now()) return true
++ return Boolean(row.isRevoked)
++ } catch (error) {
++ console.warn('[AuthSession] 浼氳瘽鐘舵€佹牎楠屽け璐ワ紝榛樿鏀捐:', error)
++ return false
++ }
++}
++
++export async function touchAuthSession(jti: string | undefined): Promise {
++ if (!jti) return
++ try {
++ await db
++ .update(authSessions)
++ .set({ lastActiveAt: new Date() })
++ .where(eq(authSessions.sessionToken, jti))
++ } catch (_error) {
++ // ignore
++ }
++}
+diff --git a/server/utils/email-verification.ts b/server/utils/email-verification.ts
+new file mode 100644
+index 0000000..d649217
+--- /dev/null
++++ b/server/utils/email-verification.ts
+@@ -0,0 +1,171 @@
++import { randomInt, timingSafeEqual } from 'node:crypto'
++import { getServerTimestamp } from '~~/server/utils/serverTime'
++import { getRedisClient, buildRedisKey } from '~~/server/utils/redis'
++
++const EMAIL_CODE_TTL_MS = 5 * 60 * 1000
++const EMAIL_CODE_COOLDOWN_MS = 60 * 1000
++const EMAIL_CODE_MAX_ATTEMPTS = 5
++const EMAIL_CODE_DAILY_LIMIT = 5
++const EMAIL_CODE_DAILY_WINDOW_MS = 24 * 60 * 60 * 1000
++
++type EmailCodeEntry = {
++ code: string
++ expiresAt: number
++ createdAt: number
++ attempts: number
++}
++
++const emailCodes = new Map()
++const dailySendCounts = new Map()
++
++const normalizeEmail = (email: string) => email.trim().toLowerCase()
++
++const getDayKey = () => {
++ const now = new Date(getServerTimestamp())
++ return `${now.getUTCFullYear()}-${now.getUTCMonth() + 1}-${now.getUTCDate()}`
++}
++
++const codeRedisKey = (email: string) => buildRedisKey('email-code', 'code', normalizeEmail(email))
++const attemptsRedisKey = (email: string) => buildRedisKey('email-code', 'attempts', normalizeEmail(email))
++const cooldownRedisKey = (email: string) => buildRedisKey('email-code', 'cooldown', normalizeEmail(email))
++const dailyRedisKey = (email: string) => buildRedisKey('email-code', 'daily', normalizeEmail(email), getDayKey())
++
++const VERIFY_EMAIL_CODE_SCRIPT = `
++local stored = redis.call('GET', KEYS[1])
++if not stored then
++ return { 0, 0 }
++end
++local attempts = redis.call('INCR', KEYS[2])
++if attempts == 1 then
++ redis.call('EXPIRE', KEYS[2], 300)
++end
++if ARGV[1] == stored then
++ redis.call('DEL', KEYS[1], KEYS[2])
++ return { 1, attempts }
++end
++if attempts >= tonumber(ARGV[2]) then
++ redis.call('DEL', KEYS[1], KEYS[2])
++ return { 2, attempts }
++end
++return { 0, attempts }
++`
++
++const safeCodeEqual = (a: string, b: string): boolean => {
++ if (a.length !== b.length) return false
++ return timingSafeEqual(Buffer.from(a), Buffer.from(b))
++}
++
++export function generateEmailCode(): string {
++ return String(randomInt(100000, 1000000))
++}
++
++export async function storeEmailCode(email: string, code: string): Promise {
++ const normalized = normalizeEmail(email)
++ const now = getServerTimestamp()
++ emailCodes.set(normalized, {
++ code,
++ expiresAt: now + EMAIL_CODE_TTL_MS,
++ createdAt: now,
++ attempts: 0
++ })
++
++ const dayKey = getDayKey()
++ const record = dailySendCounts.get(normalized)
++ if (!record || record.dayKey !== dayKey) {
++ dailySendCounts.set(normalized, { count: 1, dayKey })
++ } else {
++ record.count++
++ }
++
++ let client: Awaited