-
+
@@ -429,6 +435,17 @@
投稿人: {{ rejectSongInfo.requester }}
+
+
+
+
+
+
即将批量驳回 {{ selectedSongs.length }} 首歌曲
+
驳回后将一并清理投票、排期记录并发送通知给对应投稿人
+
+
@@ -39,7 +49,6 @@ const hasEnabledProviders = computed(() => {
})
const loginWith = (provider: string) => {
- // 外部导航到 API 端点
navigateTo(`/api/auth/${provider}`, { external: true })
}
diff --git a/app/composables/useSiteConfig.js b/app/composables/useSiteConfig.js
index 3202f60..428a851 100644
--- a/app/composables/useSiteConfig.js
+++ b/app/composables/useSiteConfig.js
@@ -19,7 +19,9 @@ const siteConfig = ref({
siteVersion: '',
submissionGuidelines: '',
icpNumber: '',
- gonganNumber: ''
+ gonganNumber: '',
+ defaultTheme: null,
+ enabledThemes: null
})
const isLoaded = ref(false)
@@ -57,7 +59,9 @@ export const useSiteConfig = () => {
gonganNumber: '',
enableReplayRequests: false,
enableCollaborativeSubmission: true,
- enableSubmissionRemarks: false
+ enableSubmissionRemarks: false,
+ defaultTheme: 'System',
+ enabledThemes: '["System","ClassicDark","ClassicLight","ModernLight"]'
}
isLoaded.value = true
} finally {
@@ -96,6 +100,32 @@ export const useSiteConfig = () => {
const enableSubmissionRemarks = computed(() => siteConfig.value.enableSubmissionRemarks === true)
const smtpEnabled = computed(() => !!siteConfig.value.smtpEnabled)
+ function parseEnabledThemes(raw) {
+ if (Array.isArray(raw)) return raw.filter(Boolean)
+ if (typeof raw === 'string') {
+ const s = raw.trim()
+ if (!s) return []
+ if (s.startsWith('[') || s.startsWith('{')) {
+ try {
+ const parsed = JSON.parse(s)
+ if (Array.isArray(parsed)) return parsed.filter(Boolean)
+ if (parsed && Array.isArray(parsed.items)) return parsed.items.filter(Boolean)
+ } catch (_) {
+ // fallback to comma split
+ }
+ }
+ return s.split(',').map((x) => x.trim()).filter(Boolean)
+ }
+ return []
+ }
+
+ const defaultTheme = computed(() =>
+ typeof siteConfig.value.defaultTheme === 'string' && siteConfig.value.defaultTheme
+ ? siteConfig.value.defaultTheme
+ : null
+ )
+ const enabledThemes = computed(() => parseEnabledThemes(siteConfig.value.enabledThemes))
+
// 初始化配置(仅在客户端执行)
const initSiteConfig = async () => {
if (typeof window !== 'undefined' && !isLoaded.value) {
@@ -126,6 +156,8 @@ export const useSiteConfig = () => {
enableCollaborativeSubmission,
enableSubmissionRemarks,
smtpEnabled,
+ defaultTheme,
+ enabledThemes,
fetchSiteConfig,
initSiteConfig,
refreshSiteConfig
diff --git a/app/composables/useTheme.ts b/app/composables/useTheme.ts
index 9c25b36..5cdca88 100644
--- a/app/composables/useTheme.ts
+++ b/app/composables/useTheme.ts
@@ -1,4 +1,4 @@
-import { computed, ref, watch } from 'vue'
+import { computed, ref, watch, nextTick } from 'vue'
const STORAGE_KEY = 'voicehub:theme'
type ThemeName = string & {}
@@ -15,6 +15,25 @@ function getMetaThemeColor() {
return document.querySelector
('meta[name="theme-color"]')
}
+function parseEnabledThemesRaw(raw: unknown): string[] {
+ if (Array.isArray(raw)) return raw.filter(Boolean) as string[]
+ if (typeof raw === 'string') {
+ const s = raw.trim()
+ if (!s) return []
+ if (s.startsWith('[') || s.startsWith('{')) {
+ try {
+ const parsed = JSON.parse(s)
+ if (Array.isArray(parsed)) return parsed.filter(Boolean) as string[]
+ if (parsed && Array.isArray((parsed as any).items)) return (parsed as any).items.filter(Boolean) as string[]
+ } catch (_) {
+ // fallback
+ }
+ }
+ return s.split(',').map((x) => x.trim()).filter(Boolean)
+ }
+ return []
+}
+
function applyTheme(theme: string) {
if (!isBrowser()) return
@@ -24,11 +43,12 @@ function applyTheme(theme: string) {
root.setAttribute('data-theme', theme)
if (appRoot) appRoot.setAttribute('data-theme', theme)
- if (theme === 'dark') {
+ const effective = resolveEffectiveTheme(theme)
+ if (effective === 'dark') {
root.classList.add('dark')
root.classList.remove('light')
root.style.colorScheme = 'dark'
- } else if (theme === 'light') {
+ } else if (effective === 'light') {
root.classList.add('light')
root.classList.remove('dark')
root.style.colorScheme = 'light'
@@ -39,11 +59,7 @@ function applyTheme(theme: string) {
const meta = getMetaThemeColor()
if (meta) {
- if (theme === 'light') {
- meta.content = '#f8fafc'
- } else {
- meta.content = '#111111'
- }
+ meta.content = effective === 'light' ? '#f8fafc' : '#111111'
}
try {
@@ -59,18 +75,37 @@ function detectSystemTheme(): 'dark' | 'light' {
return prefersDark ? 'dark' : 'light'
}
+function resolveEffectiveTheme(theme: string): 'dark' | 'light' | string {
+ if (theme === 'System' || theme === 'system') {
+ return detectSystemTheme()
+ }
+ if (theme === 'dark' || theme.endsWith('Dark')) return 'dark'
+ if (theme === 'light' || theme.endsWith('Light')) return 'light'
+ return theme
+}
+
export function useTheme() {
const config = useRuntimeConfig()
+ const { isLoaded: siteConfigLoaded, defaultTheme: siteDefaultTheme, enabledThemes: siteEnabledThemes, initSiteConfig } = useSiteConfig()
const enabledThemes = computed(() => {
- const list = (config.public?.enabledThemes as unknown) as string[] | undefined
- return Array.isArray(list) && list.length > 0 ? list : ['dark', 'light']
+ if (siteConfigLoaded.value && siteEnabledThemes.value.length > 0) {
+ return [...new Set(siteEnabledThemes.value)]
+ }
+ const fromRuntime = parseEnabledThemesRaw(config.public?.enabledThemes)
+ if (fromRuntime.length > 0) return fromRuntime
+ return ['System', 'dark', 'light']
})
const defaultTheme = computed(() => {
- const value = config.public?.defaultTheme as string | undefined
- if (value && enabledThemes.value.includes(value)) return value
- return enabledThemes.value.includes('dark') ? 'dark' : enabledThemes.value[0] || 'dark'
+ if (siteConfigLoaded.value && siteDefaultTheme.value) {
+ return siteDefaultTheme.value
+ }
+ const fromRuntime = config.public?.defaultTheme
+ if (typeof fromRuntime === 'string' && fromRuntime) {
+ return fromRuntime
+ }
+ return 'System'
})
const theme = computed({
@@ -79,21 +114,22 @@ export function useTheme() {
})
const isDark = computed(() => {
- const t = theme.value
- if (t === 'dark') return true
- if (t === 'light') return false
- return detectSystemTheme() === 'dark'
+ return resolveEffectiveTheme(theme.value) === 'dark'
})
function isThemeEnabled(name: string) {
return enabledThemes.value.includes(name)
}
+ function firstValidTheme(candidate: string): string {
+ const list = enabledThemes.value
+ if (list.includes(candidate)) return candidate
+ if (list.includes(defaultTheme.value)) return defaultTheme.value
+ return list[0] ?? 'System'
+ }
+
function setTheme(next: string, { persist = true } = {}) {
- let candidate = next
- if (!enabledThemes.value.includes(candidate)) {
- candidate = defaultTheme.value
- }
+ const candidate = firstValidTheme(next)
if (currentTheme.value === candidate && initialized.value) return
@@ -110,7 +146,7 @@ export function useTheme() {
}
function toggleTheme() {
- const order = enabledThemes.value.length >= 2 ? enabledThemes.value : ['dark', 'light']
+ const order = enabledThemes.value.length >= 2 ? enabledThemes.value : ['System', 'dark', 'light']
const current = theme.value
const idx = order.indexOf(current)
const next = order[(idx + 1) % order.length]
@@ -121,8 +157,31 @@ export function useTheme() {
setTheme(defaultTheme.value, { persist: false })
}
- function init() {
+ function onSystemThemeChange() {
+ const stored = (() => {
+ try {
+ return localStorage.getItem(STORAGE_KEY)
+ } catch {
+ return null
+ }
+ })()
+ if (!stored) {
+ applyTheme(theme.value)
+ }
+ }
+
+ async function init() {
if (initialized.value || !isBrowser()) return
+
+ if (!siteConfigLoaded.value) {
+ try {
+ await initSiteConfig()
+ await nextTick()
+ } catch (_) {
+ /* ignore */
+ }
+ }
+
initialized.value = true
let stored: string | null = null
@@ -132,34 +191,43 @@ export function useTheme() {
stored = null
}
- if (stored && enabledThemes.value.includes(stored)) {
- setTheme(stored)
- return
+ if (stored) {
+ const valid = firstValidTheme(stored)
+ if (valid === stored || enabledThemes.value.includes(stored)) {
+ setTheme(stored)
+ watchSystemTheme()
+ return
+ }
}
setTheme(defaultTheme.value, { persist: false })
+ watchSystemTheme()
+ }
- if (window.matchMedia) {
- const mql = window.matchMedia('(prefers-color-scheme: dark)')
- const onChange = () => {
- let s: string | null = null
- try {
- s = localStorage.getItem(STORAGE_KEY)
- } catch {
- s = null
- }
- if (!s) {
- setTheme(defaultTheme.value, { persist: false })
- }
- }
- if (typeof mql.addEventListener === 'function') {
- mql.addEventListener('change', onChange)
- } else if (typeof mql.addListener === 'function') {
- mql.addListener(onChange)
- }
+ function watchSystemTheme() {
+ if (!isBrowser() || !window.matchMedia) return
+ const mql = window.matchMedia('(prefers-color-scheme: dark)')
+ if (typeof mql.addEventListener === 'function') {
+ mql.addEventListener('change', onSystemThemeChange)
+ } else if (typeof (mql as any).addListener === 'function') {
+ ;(mql as any).addListener(onSystemThemeChange)
}
}
+ watch(defaultTheme, () => {
+ if (!initialized.value) return
+ const stored = (() => {
+ try {
+ return localStorage.getItem(STORAGE_KEY)
+ } catch {
+ return null
+ }
+ })()
+ if (!stored) {
+ setTheme(defaultTheme.value, { persist: false })
+ }
+ })
+
return {
theme,
currentTheme,
diff --git a/app/drizzle/migrations/20260906084643_auto-migration.sql b/app/drizzle/migrations/20260906084643_auto-migration.sql
new file mode 100644
index 0000000..e854472
--- /dev/null
+++ b/app/drizzle/migrations/20260906084643_auto-migration.sql
@@ -0,0 +1,318 @@
+CREATE TYPE "public"."card_code_status" AS ENUM('AVAILABLE', 'LOCKED', 'REDEEMED', 'INVALID');--> statement-breakpoint
+ALTER TYPE "public"."BlacklistType" ADD VALUE 'LANGUAGE';--> statement-breakpoint
+ALTER TYPE "public"."BlacklistType" ADD VALUE 'GENRE';--> statement-breakpoint
+ALTER TYPE "public"."user_status" ADD VALUE 'pending';--> statement-breakpoint
+ALTER TYPE "public"."user_status" ADD VALUE 'rejected';--> statement-breakpoint
+CREATE TABLE "auth_sessions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" integer NOT NULL,
+ "session_token" varchar(255) NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "ip_address" text,
+ "user_agent" text,
+ "last_active_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "is_revoked" boolean DEFAULT false NOT NULL,
+ "browser" text,
+ "operating_system" text,
+ "device" text,
+ "login_method" text DEFAULT 'password' NOT NULL,
+ "revoked_at" timestamp with time zone,
+ "revoked_reason" text,
+ CONSTRAINT "auth_sessions_session_token_unique" UNIQUE("session_token")
+);
+--> statement-breakpoint
+CREATE TABLE "backup_records" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "filename" text NOT NULL,
+ "file_path" text NOT NULL,
+ "file_size" bigint NOT NULL,
+ "type" text DEFAULT 'MANUAL' NOT NULL,
+ "status" text DEFAULT 'COMPLETED' NOT NULL,
+ "error_message" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "started_at" timestamp with time zone,
+ "completed_at" timestamp with time zone,
+ "created_by" integer,
+ "expires_at" timestamp with time zone
+);
+--> statement-breakpoint
+CREATE TABLE "card_code_redemptions" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "card_code_id" integer NOT NULL,
+ "user_id" integer NOT NULL,
+ "redeemed_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "song_id" integer,
+ "purpose" text NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "card_codes" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "code" varchar(64) NOT NULL,
+ "status" "card_code_status" DEFAULT 'AVAILABLE' NOT NULL,
+ "type" text NOT NULL,
+ "value" integer DEFAULT 1 NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "expires_at" timestamp with time zone,
+ "locked_at" timestamp with time zone,
+ "locked_by" integer,
+ "batch_id" varchar(64),
+ "note" text,
+ CONSTRAINT "card_codes_code_unique" UNIQUE("code")
+);
+--> statement-breakpoint
+CREATE TABLE "ChangelogEntry" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "version" text NOT NULL,
+ "title" text NOT NULL,
+ "content" text NOT NULL,
+ "published" boolean DEFAULT false NOT NULL,
+ "publishedAt" timestamp,
+ "createdBy" integer
+);
+--> statement-breakpoint
+CREATE TABLE "ExpectedPlayDate" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "date" text NOT NULL,
+ "availableSequences" text NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ CONSTRAINT "ExpectedPlayDate_date_unique" UNIQUE("date")
+);
+--> statement-breakpoint
+CREATE TABLE "GradeClass" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "grade" text NOT NULL,
+ "class" text NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ CONSTRAINT "grade_class_unique" UNIQUE("grade","class")
+);
+--> statement-breakpoint
+CREATE TABLE "oauth_providers" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "displayName" text,
+ "enabled" boolean DEFAULT true NOT NULL,
+ "clientId" text,
+ "clientSecret" text,
+ "authorizationEndpoint" text,
+ "tokenEndpoint" text,
+ "userInfoEndpoint" text,
+ "scope" text,
+ "redirectUri" text,
+ "usernameClaim" text DEFAULT 'preferred_username',
+ "nameClaim" text DEFAULT 'name',
+ "emailClaim" text DEFAULT 'email',
+ "avatarClaim" text,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL,
+ "sortOrder" integer DEFAULT 0 NOT NULL,
+ "icon" text
+);
+--> statement-breakpoint
+CREATE TABLE "passkey_challenges" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "challenge" text NOT NULL,
+ "type" text NOT NULL,
+ "user_id" integer,
+ "expires_at" timestamp with time zone NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "schedule_song_pool" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "song_id" integer NOT NULL,
+ "target_play_date" timestamp,
+ "priority" integer DEFAULT 0 NOT NULL,
+ "added_by" integer,
+ "note" text,
+ "status" text DEFAULT 'PENDING' NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "SongComment" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "songId" integer NOT NULL,
+ "userId" integer NOT NULL,
+ "content" text NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "submission_restriction_exemptions" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "user_id" integer NOT NULL,
+ "exemption_type" text NOT NULL,
+ "reason" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "expires_at" timestamp with time zone,
+ "created_by" integer,
+ CONSTRAINT "submission_restriction_exemptions_user_type_unique" UNIQUE("user_id","exemption_type")
+);
+--> statement-breakpoint
+CREATE TABLE "user_passkeys" (
+ "id" text PRIMARY KEY NOT NULL,
+ "user_id" integer NOT NULL,
+ "name" text,
+ "public_key" text NOT NULL,
+ "credential_device_type" text NOT NULL,
+ "credential_backed_up" boolean NOT NULL,
+ "transports" text,
+ "counter" integer DEFAULT 0 NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "last_used_at" timestamp with time zone
+);
+--> statement-breakpoint
+ALTER TABLE "User" ALTER COLUMN "forcePasswordChange" SET DEFAULT false;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "batch_id" uuid;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "source" text DEFAULT 'SYSTEM' NOT NULL;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "sender_id" integer;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "sender_name" text;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "sender_username" text;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "title" text;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "important" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "Notification" ADD COLUMN "user_deleted" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "Schedule" ADD COLUMN "replay_request_id" integer;--> statement-breakpoint
+ALTER TABLE "Song" ADD COLUMN "expectedPlayDate" text;--> statement-breakpoint
+ALTER TABLE "Song" ADD COLUMN "expectedPlaySequence" integer;--> statement-breakpoint
+ALTER TABLE "Song" ADD COLUMN "durationSeconds" integer;--> statement-breakpoint
+ALTER TABLE "Song" ADD COLUMN "submissionNotePublicStatus" text;--> statement-breakpoint
+ALTER TABLE "Song" ADD COLUMN "cardCodeId" integer;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "siteVersion" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionNoteRequiresApproval" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "instanceId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "telemetryEnabled" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "showBeianIcon" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysDisplay" integer DEFAULT 7 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysEditable" integer DEFAULT 3 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysRequestable" integer DEFAULT 14 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysBeforeEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysBefore" integer DEFAULT 1 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysAfterEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "scheduleDaysAfter" integer DEFAULT 1 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "forcePasswordChangeOnFirstLogin" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "enableSubmissionRestriction" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionScope" text DEFAULT 'all' NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "sameSongRestrictionHours" integer;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "sameArtistRestrictionHours" integer;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "cardCodeEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "cardCodeSubmissionRequired" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "cardCodeVoteEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "cardCodeReplayEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "enableCardCodeRequests" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "requireCardCodeForRequests" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "enableCardCodeLimitBypass" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionGradeEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionClassEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionGraduateEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "submissionRestrictionWithdrawnEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "captchaProvider" text DEFAULT 'TURNSTILE';--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "captchaEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "turnstileSiteKey" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "turnstileSecretKey" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "allowRegister" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerRequireEmail" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerRequireEmailVerify" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerDefaultRole" text DEFAULT 'USER';--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerEnabledGradeClasses" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerRequiresApproval" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthRegisterRequiresApproval" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerEmailRequired" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "registerRequiresGradeClass" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthAllowRegister" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthAllowLogin" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthAllowBind" boolean DEFAULT true NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "allowOAuthRegistration" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthRedirectUri" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthStateSecret" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "oauthProviders" text DEFAULT '[]';--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "githubOAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "githubClientId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "githubClientSecret" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "casdoorOAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "casdoorServerUrl" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "casdoorClientId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "casdoorClientSecret" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "casdoorOrganizationName" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "googleOAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "googleClientId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "googleClientSecret" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "aggregateOAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "aggregateOAuthAppId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "aggregateOAuthAppKey" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "aggregateOAuthLoginType" text DEFAULT 'qq';--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "aggregateOAuthEndpoint" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthDisplayName" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthAuthorizeUrl" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthTokenUrl" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthUserInfoUrl" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthScope" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthClientId" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthClientSecret" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthUserIdField" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthUsernameField" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthNameField" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthEmailField" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "customOAuthAvatarField" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "captchaMaxFailures" integer DEFAULT 3 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "autoBackupEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "autoBackupIntervalHours" integer DEFAULT 24 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "autoBackupRetentionDays" integer DEFAULT 30 NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "autoBackupConfig" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "statisticsCode" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "statisticsCodeEnabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "defaultTheme" text DEFAULT 'System' NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "enabledThemes" text DEFAULT '["System","ClassicDark","ClassicLight","ModernLight"]' NOT NULL;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "enabledPlatforms" text;--> statement-breakpoint
+ALTER TABLE "SystemSettings" ADD COLUMN "platformOrder" text;--> statement-breakpoint
+ALTER TABLE "User" ADD COLUMN "studentId" text;--> statement-breakpoint
+ALTER TABLE "User" ADD COLUMN "tokenVersion" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
+ALTER TABLE "User" ADD COLUMN "avatarProvider" text;--> statement-breakpoint
+ALTER TABLE "User" ADD COLUMN "avatarProviderUserId" text;--> statement-breakpoint
+ALTER TABLE "User" ADD COLUMN "remark" text;--> statement-breakpoint
+ALTER TABLE "auth_sessions" ADD CONSTRAINT "auth_sessions_user_id_User_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "card_code_redemptions" ADD CONSTRAINT "card_code_redemptions_card_code_id_card_codes_id_fk" FOREIGN KEY ("card_code_id") REFERENCES "public"."card_codes"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "card_code_redemptions" ADD CONSTRAINT "card_code_redemptions_user_id_User_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "card_code_redemptions" ADD CONSTRAINT "card_code_redemptions_song_id_Song_id_fk" FOREIGN KEY ("song_id") REFERENCES "public"."Song"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "schedule_song_pool" ADD CONSTRAINT "schedule_song_pool_song_id_Song_id_fk" FOREIGN KEY ("song_id") REFERENCES "public"."Song"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "submission_restriction_exemptions" ADD CONSTRAINT "submission_restriction_exemptions_user_id_User_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_passkeys" ADD CONSTRAINT "user_passkeys_user_id_User_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."User"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "auth_sessions_user_active_idx" ON "auth_sessions" USING btree ("user_id","is_revoked","expires_at");--> statement-breakpoint
+CREATE INDEX "auth_sessions_expires_at_idx" ON "auth_sessions" USING btree ("expires_at");--> statement-breakpoint
+CREATE INDEX "auth_sessions_last_active_idx" ON "auth_sessions" USING btree ("last_active_at");--> statement-breakpoint
+CREATE UNIQUE INDEX "auth_sessions_session_token_unique" ON "auth_sessions" USING btree ("session_token");--> statement-breakpoint
+CREATE INDEX "backup_records_created_at_idx" ON "backup_records" USING btree ("created_at");--> statement-breakpoint
+CREATE INDEX "backup_records_status_idx" ON "backup_records" USING btree ("status");--> statement-breakpoint
+CREATE INDEX "card_code_redemptions_user_id_idx" ON "card_code_redemptions" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "card_code_redemptions_card_code_id_idx" ON "card_code_redemptions" USING btree ("card_code_id");--> statement-breakpoint
+CREATE INDEX "card_codes_status_idx" ON "card_codes" USING btree ("status");--> statement-breakpoint
+CREATE INDEX "card_codes_batch_id_idx" ON "card_codes" USING btree ("batch_id");--> statement-breakpoint
+CREATE INDEX "card_codes_expires_at_idx" ON "card_codes" USING btree ("expires_at");--> statement-breakpoint
+CREATE INDEX "passkey_challenges_challenge_idx" ON "passkey_challenges" USING btree ("challenge");--> statement-breakpoint
+CREATE INDEX "passkey_challenges_expires_at_idx" ON "passkey_challenges" USING btree ("expires_at");--> statement-breakpoint
+CREATE INDEX "schedule_song_pool_song_id_idx" ON "schedule_song_pool" USING btree ("song_id");--> statement-breakpoint
+CREATE INDEX "schedule_song_pool_target_date_idx" ON "schedule_song_pool" USING btree ("target_play_date");--> statement-breakpoint
+CREATE INDEX "submission_restriction_exemptions_user_id_idx" ON "submission_restriction_exemptions" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "user_passkeys_user_id_idx" ON "user_passkeys" USING btree ("user_id");--> statement-breakpoint
+ALTER TABLE "Song" ADD CONSTRAINT "Song_cardCodeId_card_codes_id_fk" FOREIGN KEY ("cardCodeId") REFERENCES "public"."card_codes"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "notification_user_important_read_created_idx" ON "Notification" USING btree ("userId","important","read","createdAt");--> statement-breakpoint
+CREATE INDEX "notification_batch_id_idx" ON "Notification" USING btree ("batch_id");--> statement-breakpoint
+CREATE INDEX "schedule_published_song_idx" ON "Schedule" USING btree ("publishedAt","songId");--> statement-breakpoint
+CREATE INDEX "schedule_published_date_idx" ON "Schedule" USING btree ("publishedAt","playDate");--> statement-breakpoint
+CREATE INDEX "song_card_code_id_idx" ON "Song" USING btree ("cardCodeId");--> statement-breakpoint
+CREATE INDEX "song_semester_created_at_idx" ON "Song" USING btree ("semester","createdAt");--> statement-breakpoint
+CREATE INDEX "song_requester_id_idx" ON "Song" USING btree ("requesterId");--> statement-breakpoint
+CREATE UNIQUE INDEX "User_username_unique" ON "User" USING btree ("username");--> statement-breakpoint
+CREATE INDEX "vote_user_song_idx" ON "Vote" USING btree ("userId","songId");--> statement-breakpoint
+ALTER TABLE "Vote" ADD CONSTRAINT "vote_song_user_unique" UNIQUE("songId","userId");
\ No newline at end of file
diff --git a/app/drizzle/migrations/meta/20260906084643_snapshot.json b/app/drizzle/migrations/meta/20260906084643_snapshot.json
new file mode 100644
index 0000000..3d7de32
--- /dev/null
+++ b/app/drizzle/migrations/meta/20260906084643_snapshot.json
@@ -0,0 +1,3953 @@
+{
+ "id": "e55ad241-91ce-4c7e-9d3a-79433b19976e",
+ "prevId": "9a604776-4a2c-47e2-ac0f-87bdc9c94abf",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.api_key_permissions": {
+ "name": "api_key_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_keys": {
+ "name": "api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_prefix": {
+ "name": "key_prefix",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "usage_count": {
+ "name": "usage_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_logs": {
+ "name": "api_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "endpoint": {
+ "name": "endpoint",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "method": {
+ "name": "method",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_time_ms": {
+ "name": "response_time_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_body": {
+ "name": "request_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_body": {
+ "name": "response_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_sessions": {
+ "name": "auth_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_active_at": {
+ "name": "last_active_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "is_revoked": {
+ "name": "is_revoked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "browser": {
+ "name": "browser",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operating_system": {
+ "name": "operating_system",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "device": {
+ "name": "device",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "login_method": {
+ "name": "login_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'password'"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_reason": {
+ "name": "revoked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "auth_sessions_user_active_idx": {
+ "name": "auth_sessions_user_active_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "is_revoked",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auth_sessions_expires_at_idx": {
+ "name": "auth_sessions_expires_at_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auth_sessions_last_active_idx": {
+ "name": "auth_sessions_last_active_idx",
+ "columns": [
+ {
+ "expression": "last_active_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "auth_sessions_session_token_unique": {
+ "name": "auth_sessions_session_token_unique",
+ "columns": [
+ {
+ "expression": "session_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auth_sessions_user_id_User_id_fk": {
+ "name": "auth_sessions_user_id_User_id_fk",
+ "tableFrom": "auth_sessions",
+ "tableTo": "User",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "auth_sessions_session_token_unique": {
+ "name": "auth_sessions_session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "session_token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.backup_records": {
+ "name": "backup_records",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_path": {
+ "name": "file_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_size": {
+ "name": "file_size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'MANUAL'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'COMPLETED'"
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "backup_records_created_at_idx": {
+ "name": "backup_records_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "backup_records_status_idx": {
+ "name": "backup_records_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.card_code_redemptions": {
+ "name": "card_code_redemptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "card_code_id": {
+ "name": "card_code_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redeemed_at": {
+ "name": "redeemed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "song_id": {
+ "name": "song_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "purpose": {
+ "name": "purpose",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "card_code_redemptions_user_id_idx": {
+ "name": "card_code_redemptions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "card_code_redemptions_card_code_id_idx": {
+ "name": "card_code_redemptions_card_code_id_idx",
+ "columns": [
+ {
+ "expression": "card_code_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "card_code_redemptions_card_code_id_card_codes_id_fk": {
+ "name": "card_code_redemptions_card_code_id_card_codes_id_fk",
+ "tableFrom": "card_code_redemptions",
+ "tableTo": "card_codes",
+ "columnsFrom": [
+ "card_code_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_code_redemptions_user_id_User_id_fk": {
+ "name": "card_code_redemptions_user_id_User_id_fk",
+ "tableFrom": "card_code_redemptions",
+ "tableTo": "User",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "card_code_redemptions_song_id_Song_id_fk": {
+ "name": "card_code_redemptions_song_id_Song_id_fk",
+ "tableFrom": "card_code_redemptions",
+ "tableTo": "Song",
+ "columnsFrom": [
+ "song_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.card_codes": {
+ "name": "card_codes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "card_code_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'AVAILABLE'"
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_by": {
+ "name": "locked_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "batch_id": {
+ "name": "batch_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "note": {
+ "name": "note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "card_codes_status_idx": {
+ "name": "card_codes_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "card_codes_batch_id_idx": {
+ "name": "card_codes_batch_id_idx",
+ "columns": [
+ {
+ "expression": "batch_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "card_codes_expires_at_idx": {
+ "name": "card_codes_expires_at_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "card_codes_code_unique": {
+ "name": "card_codes_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ChangelogEntry": {
+ "name": "ChangelogEntry",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published": {
+ "name": "published",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "publishedAt": {
+ "name": "publishedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collaboration_logs": {
+ "name": "collaboration_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "collaborator_id": {
+ "name": "collaborator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "operator_id": {
+ "name": "operator_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.EmailTemplate": {
+ "name": "EmailTemplate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(200)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "varchar(300)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "html": {
+ "name": "html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updatedByUserId": {
+ "name": "updatedByUserId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ExpectedPlayDate": {
+ "name": "ExpectedPlayDate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "date": {
+ "name": "date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "availableSequences": {
+ "name": "availableSequences",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ExpectedPlayDate_date_unique": {
+ "name": "ExpectedPlayDate_date_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "date"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.GradeClass": {
+ "name": "GradeClass",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "grade": {
+ "name": "grade",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "class": {
+ "name": "class",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "grade_class_unique": {
+ "name": "grade_class_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "grade",
+ "class"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.NotificationSettings": {
+ "name": "NotificationSettings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "userId": {
+ "name": "userId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "songRequestEnabled": {
+ "name": "songRequestEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "songVotedEnabled": {
+ "name": "songVotedEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "songPlayedEnabled": {
+ "name": "songPlayedEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "refreshInterval": {
+ "name": "refreshInterval",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "songVotedThreshold": {
+ "name": "songVotedThreshold",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.Notification": {
+ "name": "Notification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "read": {
+ "name": "read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "songId": {
+ "name": "songId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "batch_id": {
+ "name": "batch_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'SYSTEM'"
+ },
+ "sender_id": {
+ "name": "sender_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sender_name": {
+ "name": "sender_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sender_username": {
+ "name": "sender_username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "important": {
+ "name": "important",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "user_deleted": {
+ "name": "user_deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "notification_user_important_read_created_idx": {
+ "name": "notification_user_important_read_created_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "important",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "read",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "createdAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_batch_id_idx": {
+ "name": "notification_batch_id_idx",
+ "columns": [
+ {
+ "expression": "batch_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.oauth_providers": {
+ "name": "oauth_providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "displayName": {
+ "name": "displayName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "clientId": {
+ "name": "clientId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "clientSecret": {
+ "name": "clientSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "authorizationEndpoint": {
+ "name": "authorizationEndpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tokenEndpoint": {
+ "name": "tokenEndpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userInfoEndpoint": {
+ "name": "userInfoEndpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redirectUri": {
+ "name": "redirectUri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "usernameClaim": {
+ "name": "usernameClaim",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'preferred_username'"
+ },
+ "nameClaim": {
+ "name": "nameClaim",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'name'"
+ },
+ "emailClaim": {
+ "name": "emailClaim",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'email'"
+ },
+ "avatarClaim": {
+ "name": "avatarClaim",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "sortOrder": {
+ "name": "sortOrder",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.passkey_challenges": {
+ "name": "passkey_challenges",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "challenge": {
+ "name": "challenge",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "passkey_challenges_challenge_idx": {
+ "name": "passkey_challenges_challenge_idx",
+ "columns": [
+ {
+ "expression": "challenge",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "passkey_challenges_expires_at_idx": {
+ "name": "passkey_challenges_expires_at_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.PlayTime": {
+ "name": "PlayTime",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "startTime": {
+ "name": "startTime",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "endTime": {
+ "name": "endTime",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.RequestTime": {
+ "name": "RequestTime",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp(6)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp(6)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "startTime": {
+ "name": "startTime",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "endTime": {
+ "name": "endTime",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expected": {
+ "name": "expected",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "accepted": {
+ "name": "accepted",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "past": {
+ "name": "past",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.schedule_song_pool": {
+ "name": "schedule_song_pool",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "song_id": {
+ "name": "song_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_play_date": {
+ "name": "target_play_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "added_by": {
+ "name": "added_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "note": {
+ "name": "note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'PENDING'"
+ }
+ },
+ "indexes": {
+ "schedule_song_pool_song_id_idx": {
+ "name": "schedule_song_pool_song_id_idx",
+ "columns": [
+ {
+ "expression": "song_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "schedule_song_pool_target_date_idx": {
+ "name": "schedule_song_pool_target_date_idx",
+ "columns": [
+ {
+ "expression": "target_play_date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "schedule_song_pool_song_id_Song_id_fk": {
+ "name": "schedule_song_pool_song_id_Song_id_fk",
+ "tableFrom": "schedule_song_pool",
+ "tableTo": "Song",
+ "columnsFrom": [
+ "song_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.Schedule": {
+ "name": "Schedule",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "songId": {
+ "name": "songId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "playDate": {
+ "name": "playDate",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "played": {
+ "name": "played",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "sequence": {
+ "name": "sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "playTimeId": {
+ "name": "playTimeId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isDraft": {
+ "name": "isDraft",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "publishedAt": {
+ "name": "publishedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "replay_request_id": {
+ "name": "replay_request_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "schedule_published_song_idx": {
+ "name": "schedule_published_song_idx",
+ "columns": [
+ {
+ "expression": "publishedAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "songId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "schedule_published_date_idx": {
+ "name": "schedule_published_date_idx",
+ "columns": [
+ {
+ "expression": "publishedAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "playDate",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.Semester": {
+ "name": "Semester",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isActive": {
+ "name": "isActive",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.SongBlacklist": {
+ "name": "SongBlacklist",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "type": {
+ "name": "type",
+ "type": "BlacklistType",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isActive": {
+ "name": "isActive",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.song_collaborators": {
+ "name": "song_collaborators",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "song_id": {
+ "name": "song_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "collaborator_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'PENDING'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.SongComment": {
+ "name": "SongComment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "songId": {
+ "name": "songId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.song_replay_requests": {
+ "name": "song_replay_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "song_id": {
+ "name": "song_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "status": {
+ "name": "status",
+ "type": "replay_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'PENDING'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "song_replay_requests_song_id_user_id_unique": {
+ "name": "song_replay_requests_song_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "song_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.Song": {
+ "name": "Song",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "kg_hash": {
+ "name": "kg_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "singer": {
+ "name": "singer",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_url": {
+ "name": "cover_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "artist": {
+ "name": "artist",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requesterId": {
+ "name": "requesterId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "played": {
+ "name": "played",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "playedAt": {
+ "name": "playedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "semester": {
+ "name": "semester",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "preferredPlayTimeId": {
+ "name": "preferredPlayTimeId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover": {
+ "name": "cover",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "playUrl": {
+ "name": "playUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "musicPlatform": {
+ "name": "musicPlatform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "musicId": {
+ "name": "musicId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "submissionNote": {
+ "name": "submissionNote",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "submissionNotePublic": {
+ "name": "submissionNotePublic",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "hitRequestId": {
+ "name": "hitRequestId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expectedPlayDate": {
+ "name": "expectedPlayDate",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expectedPlaySequence": {
+ "name": "expectedPlaySequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "durationSeconds": {
+ "name": "durationSeconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "submissionNotePublicStatus": {
+ "name": "submissionNotePublicStatus",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cardCodeId": {
+ "name": "cardCodeId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "song_card_code_id_idx": {
+ "name": "song_card_code_id_idx",
+ "columns": [
+ {
+ "expression": "cardCodeId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "song_semester_created_at_idx": {
+ "name": "song_semester_created_at_idx",
+ "columns": [
+ {
+ "expression": "semester",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "createdAt",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "song_requester_id_idx": {
+ "name": "song_requester_id_idx",
+ "columns": [
+ {
+ "expression": "requesterId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "Song_cardCodeId_card_codes_id_fk": {
+ "name": "Song_cardCodeId_card_codes_id_fk",
+ "tableFrom": "Song",
+ "tableTo": "card_codes",
+ "columnsFrom": [
+ "cardCodeId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.submission_restriction_exemptions": {
+ "name": "submission_restriction_exemptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "exemption_type": {
+ "name": "exemption_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "submission_restriction_exemptions_user_id_idx": {
+ "name": "submission_restriction_exemptions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "submission_restriction_exemptions_user_id_User_id_fk": {
+ "name": "submission_restriction_exemptions_user_id_User_id_fk",
+ "tableFrom": "submission_restriction_exemptions",
+ "tableTo": "User",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "submission_restriction_exemptions_user_type_unique": {
+ "name": "submission_restriction_exemptions_user_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id",
+ "exemption_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.SystemSettings": {
+ "name": "SystemSettings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "enablePlayTimeSelection": {
+ "name": "enablePlayTimeSelection",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "siteTitle": {
+ "name": "siteTitle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "siteLogoUrl": {
+ "name": "siteLogoUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "schoolLogoHomeUrl": {
+ "name": "schoolLogoHomeUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "schoolLogoPrintUrl": {
+ "name": "schoolLogoPrintUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "siteDescription": {
+ "name": "siteDescription",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "siteVersion": {
+ "name": "siteVersion",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "submissionGuidelines": {
+ "name": "submissionGuidelines",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icpNumber": {
+ "name": "icpNumber",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gonganNumber": {
+ "name": "gonganNumber",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enableSubmissionLimit": {
+ "name": "enableSubmissionLimit",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "dailySubmissionLimit": {
+ "name": "dailySubmissionLimit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weeklySubmissionLimit": {
+ "name": "weeklySubmissionLimit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "monthlySubmissionLimit": {
+ "name": "monthlySubmissionLimit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "showBlacklistKeywords": {
+ "name": "showBlacklistKeywords",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "hideStudentInfo": {
+ "name": "hideStudentInfo",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "smtpEnabled": {
+ "name": "smtpEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "smtpHost": {
+ "name": "smtpHost",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtpPort": {
+ "name": "smtpPort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 587
+ },
+ "smtpSecure": {
+ "name": "smtpSecure",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "smtpUsername": {
+ "name": "smtpUsername",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtpPassword": {
+ "name": "smtpPassword",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtpFromEmail": {
+ "name": "smtpFromEmail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtpFromName": {
+ "name": "smtpFromName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'校园广播站'"
+ },
+ "enableRequestTimeLimitation": {
+ "name": "enableRequestTimeLimitation",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "forceBlockAllRequests": {
+ "name": "forceBlockAllRequests",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableReplayRequests": {
+ "name": "enableReplayRequests",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableCollaborativeSubmission": {
+ "name": "enableCollaborativeSubmission",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "enableSubmissionRemarks": {
+ "name": "enableSubmissionRemarks",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionNoteRequiresApproval": {
+ "name": "submissionNoteRequiresApproval",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "instanceId": {
+ "name": "instanceId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "telemetryEnabled": {
+ "name": "telemetryEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "showBeianIcon": {
+ "name": "showBeianIcon",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "scheduleDaysDisplay": {
+ "name": "scheduleDaysDisplay",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 7
+ },
+ "scheduleDaysEditable": {
+ "name": "scheduleDaysEditable",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 3
+ },
+ "scheduleDaysRequestable": {
+ "name": "scheduleDaysRequestable",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 14
+ },
+ "scheduleDaysBeforeEnabled": {
+ "name": "scheduleDaysBeforeEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "scheduleDaysBefore": {
+ "name": "scheduleDaysBefore",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "scheduleDaysAfterEnabled": {
+ "name": "scheduleDaysAfterEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "scheduleDaysAfter": {
+ "name": "scheduleDaysAfter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "forcePasswordChangeOnFirstLogin": {
+ "name": "forcePasswordChangeOnFirstLogin",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableSubmissionRestriction": {
+ "name": "enableSubmissionRestriction",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionScope": {
+ "name": "submissionRestrictionScope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'all'"
+ },
+ "sameSongRestrictionHours": {
+ "name": "sameSongRestrictionHours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sameArtistRestrictionHours": {
+ "name": "sameArtistRestrictionHours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cardCodeEnabled": {
+ "name": "cardCodeEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cardCodeSubmissionRequired": {
+ "name": "cardCodeSubmissionRequired",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cardCodeVoteEnabled": {
+ "name": "cardCodeVoteEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cardCodeReplayEnabled": {
+ "name": "cardCodeReplayEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableCardCodeRequests": {
+ "name": "enableCardCodeRequests",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "requireCardCodeForRequests": {
+ "name": "requireCardCodeForRequests",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enableCardCodeLimitBypass": {
+ "name": "enableCardCodeLimitBypass",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionEnabled": {
+ "name": "submissionRestrictionEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionGradeEnabled": {
+ "name": "submissionRestrictionGradeEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionClassEnabled": {
+ "name": "submissionRestrictionClassEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionGraduateEnabled": {
+ "name": "submissionRestrictionGraduateEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "submissionRestrictionWithdrawnEnabled": {
+ "name": "submissionRestrictionWithdrawnEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "captchaProvider": {
+ "name": "captchaProvider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'TURNSTILE'"
+ },
+ "captchaEnabled": {
+ "name": "captchaEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "turnstileSiteKey": {
+ "name": "turnstileSiteKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "turnstileSecretKey": {
+ "name": "turnstileSecretKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "allowRegister": {
+ "name": "allowRegister",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "registerRequireEmail": {
+ "name": "registerRequireEmail",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "registerRequireEmailVerify": {
+ "name": "registerRequireEmailVerify",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "registerDefaultRole": {
+ "name": "registerDefaultRole",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'USER'"
+ },
+ "registerEnabledGradeClasses": {
+ "name": "registerEnabledGradeClasses",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "registerRequiresApproval": {
+ "name": "registerRequiresApproval",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "oauthRegisterRequiresApproval": {
+ "name": "oauthRegisterRequiresApproval",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "registerEmailRequired": {
+ "name": "registerEmailRequired",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "registerRequiresGradeClass": {
+ "name": "registerRequiresGradeClass",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "oauthEnabled": {
+ "name": "oauthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "oauthAllowRegister": {
+ "name": "oauthAllowRegister",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "oauthAllowLogin": {
+ "name": "oauthAllowLogin",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "oauthAllowBind": {
+ "name": "oauthAllowBind",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "allowOAuthRegistration": {
+ "name": "allowOAuthRegistration",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "oauthRedirectUri": {
+ "name": "oauthRedirectUri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "oauthStateSecret": {
+ "name": "oauthStateSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "oauthProviders": {
+ "name": "oauthProviders",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "githubOAuthEnabled": {
+ "name": "githubOAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "githubClientId": {
+ "name": "githubClientId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "githubClientSecret": {
+ "name": "githubClientSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "casdoorOAuthEnabled": {
+ "name": "casdoorOAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "casdoorServerUrl": {
+ "name": "casdoorServerUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "casdoorClientId": {
+ "name": "casdoorClientId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "casdoorClientSecret": {
+ "name": "casdoorClientSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "casdoorOrganizationName": {
+ "name": "casdoorOrganizationName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "googleOAuthEnabled": {
+ "name": "googleOAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "googleClientId": {
+ "name": "googleClientId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "googleClientSecret": {
+ "name": "googleClientSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aggregateOAuthEnabled": {
+ "name": "aggregateOAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "aggregateOAuthAppId": {
+ "name": "aggregateOAuthAppId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aggregateOAuthAppKey": {
+ "name": "aggregateOAuthAppKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aggregateOAuthLoginType": {
+ "name": "aggregateOAuthLoginType",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'qq'"
+ },
+ "aggregateOAuthEndpoint": {
+ "name": "aggregateOAuthEndpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthEnabled": {
+ "name": "customOAuthEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "customOAuthDisplayName": {
+ "name": "customOAuthDisplayName",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthAuthorizeUrl": {
+ "name": "customOAuthAuthorizeUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthTokenUrl": {
+ "name": "customOAuthTokenUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthUserInfoUrl": {
+ "name": "customOAuthUserInfoUrl",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthScope": {
+ "name": "customOAuthScope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthClientId": {
+ "name": "customOAuthClientId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthClientSecret": {
+ "name": "customOAuthClientSecret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthUserIdField": {
+ "name": "customOAuthUserIdField",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthUsernameField": {
+ "name": "customOAuthUsernameField",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthNameField": {
+ "name": "customOAuthNameField",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthEmailField": {
+ "name": "customOAuthEmailField",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "customOAuthAvatarField": {
+ "name": "customOAuthAvatarField",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "captchaMaxFailures": {
+ "name": "captchaMaxFailures",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 3
+ },
+ "autoBackupEnabled": {
+ "name": "autoBackupEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "autoBackupIntervalHours": {
+ "name": "autoBackupIntervalHours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 24
+ },
+ "autoBackupRetentionDays": {
+ "name": "autoBackupRetentionDays",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 30
+ },
+ "autoBackupConfig": {
+ "name": "autoBackupConfig",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "statisticsCode": {
+ "name": "statisticsCode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "statisticsCodeEnabled": {
+ "name": "statisticsCodeEnabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "defaultTheme": {
+ "name": "defaultTheme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'System'"
+ },
+ "enabledThemes": {
+ "name": "enabledThemes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[\"System\",\"ClassicDark\",\"ClassicLight\",\"ModernLight\"]'"
+ },
+ "enabledPlatforms": {
+ "name": "enabledPlatforms",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platformOrder": {
+ "name": "platformOrder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.UserIdentity": {
+ "name": "UserIdentity",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerUserId": {
+ "name": "providerUserId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerUsername": {
+ "name": "providerUsername",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "UserIdentity_userId_User_id_fk": {
+ "name": "UserIdentity_userId_User_id_fk",
+ "tableFrom": "UserIdentity",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UserIdentity_provider_providerUserId_unique": {
+ "name": "UserIdentity_provider_providerUserId_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider",
+ "providerUserId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_passkeys": {
+ "name": "user_passkeys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_key": {
+ "name": "public_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_device_type": {
+ "name": "credential_device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_backed_up": {
+ "name": "credential_backed_up",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "user_passkeys_user_id_idx": {
+ "name": "user_passkeys_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_passkeys_user_id_User_id_fk": {
+ "name": "user_passkeys_user_id_User_id_fk",
+ "tableFrom": "user_passkeys",
+ "tableTo": "User",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_status_logs": {
+ "name": "user_status_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "old_status": {
+ "name": "old_status",
+ "type": "user_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_status": {
+ "name": "new_status",
+ "type": "user_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operator_id": {
+ "name": "operator_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.User": {
+ "name": "User",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grade": {
+ "name": "grade",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class": {
+ "name": "class",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "studentId": {
+ "name": "studentId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'USER'"
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "lastLogin": {
+ "name": "lastLogin",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastLoginIp": {
+ "name": "lastLoginIp",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "passwordChangedAt": {
+ "name": "passwordChangedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "forcePasswordChange": {
+ "name": "forcePasswordChange",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "meowNickname": {
+ "name": "meowNickname",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "meowBoundAt": {
+ "name": "meowBoundAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "user_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "statusChangedAt": {
+ "name": "statusChangedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "statusChangedBy": {
+ "name": "statusChangedBy",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tokenVersion": {
+ "name": "tokenVersion",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "avatarProvider": {
+ "name": "avatarProvider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avatarProviderUserId": {
+ "name": "avatarProviderUserId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remark": {
+ "name": "remark",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "User_username_unique": {
+ "name": "User_username_unique",
+ "columns": [
+ {
+ "expression": "username",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.Vote": {
+ "name": "Vote",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "songId": {
+ "name": "songId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "vote_user_song_idx": {
+ "name": "vote_user_song_idx",
+ "columns": [
+ {
+ "expression": "userId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "songId",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "vote_song_user_unique": {
+ "name": "vote_song_user_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "songId",
+ "userId"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.BlacklistType": {
+ "name": "BlacklistType",
+ "schema": "public",
+ "values": [
+ "SONG",
+ "KEYWORD",
+ "LANGUAGE",
+ "GENRE"
+ ]
+ },
+ "public.card_code_status": {
+ "name": "card_code_status",
+ "schema": "public",
+ "values": [
+ "AVAILABLE",
+ "LOCKED",
+ "REDEEMED",
+ "INVALID"
+ ]
+ },
+ "public.collaborator_status": {
+ "name": "collaborator_status",
+ "schema": "public",
+ "values": [
+ "PENDING",
+ "ACCEPTED",
+ "REJECTED"
+ ]
+ },
+ "public.replay_request_status": {
+ "name": "replay_request_status",
+ "schema": "public",
+ "values": [
+ "PENDING",
+ "FULFILLED",
+ "REJECTED"
+ ]
+ },
+ "public.user_status": {
+ "name": "user_status",
+ "schema": "public",
+ "values": [
+ "active",
+ "withdrawn",
+ "graduate",
+ "pending",
+ "rejected"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/app/drizzle/migrations/meta/_journal.json b/app/drizzle/migrations/meta/_journal.json
index fd5d1f7..654606c 100644
--- a/app/drizzle/migrations/meta/_journal.json
+++ b/app/drizzle/migrations/meta/_journal.json
@@ -141,6 +141,13 @@
"when": 1776518400000,
"tag": "20260418112000_add_song_comments",
"breakpoints": true
+ },
+ {
+ "idx": 20,
+ "version": "7",
+ "when": 1788684403309,
+ "tag": "20260906084643_auto-migration",
+ "breakpoints": true
}
]
-}
+}
\ No newline at end of file
diff --git a/app/drizzle/schema.ts b/app/drizzle/schema.ts
index f705696..eb7b324 100644
--- a/app/drizzle/schema.ts
+++ b/app/drizzle/schema.ts
@@ -51,9 +51,16 @@ export const authSessions = pgTable('auth_sessions', {
userAgent: text('user_agent'),
lastActiveAt: timestamp('last_active_at', { withTimezone: true }).defaultNow().notNull(),
isRevoked: boolean('is_revoked').default(false).notNull(),
+ browser: text('browser'),
+ operatingSystem: text('operating_system'),
+ device: text('device'),
+ loginMethod: text('login_method').default('password').notNull(),
+ revokedAt: timestamp('revoked_at', { withTimezone: true }),
+ revokedReason: text('revoked_reason')
}, (table) => [
- index('auth_sessions_user_id_idx').on(table.userId),
+ index('auth_sessions_user_active_idx').on(table.userId, table.isRevoked, table.expiresAt),
index('auth_sessions_expires_at_idx').on(table.expiresAt),
+ index('auth_sessions_last_active_idx').on(table.lastActiveAt),
uniqueIndex('auth_sessions_session_token_unique').on(table.sessionToken),
]);
@@ -258,16 +265,29 @@ export const systemSettings = pgTable('SystemSettings', {
enableReplayRequests: boolean('enableReplayRequests').default(false).notNull(),
enableCollaborativeSubmission: boolean('enableCollaborativeSubmission').default(true).notNull(),
enableSubmissionRemarks: boolean('enableSubmissionRemarks').default(false).notNull(),
+ submissionNoteRequiresApproval: boolean('submissionNoteRequiresApproval').default(false).notNull(),
instanceId: text('instanceId'),
telemetryEnabled: boolean('telemetryEnabled').default(true).notNull(),
showBeianIcon: boolean('showBeianIcon').default(true).notNull(),
scheduleDaysDisplay: integer('scheduleDaysDisplay').default(7).notNull(),
scheduleDaysEditable: integer('scheduleDaysEditable').default(3).notNull(),
scheduleDaysRequestable: integer('scheduleDaysRequestable').default(14).notNull(),
+ scheduleDaysBeforeEnabled: boolean('scheduleDaysBeforeEnabled').default(false).notNull(),
+ scheduleDaysBefore: integer('scheduleDaysBefore').default(1).notNull(),
+ scheduleDaysAfterEnabled: boolean('scheduleDaysAfterEnabled').default(false).notNull(),
+ scheduleDaysAfter: integer('scheduleDaysAfter').default(1).notNull(),
+ forcePasswordChangeOnFirstLogin: boolean('forcePasswordChangeOnFirstLogin').default(false).notNull(),
+ enableSubmissionRestriction: boolean('enableSubmissionRestriction').default(false).notNull(),
+ submissionRestrictionScope: text('submissionRestrictionScope').default('all').notNull(),
+ sameSongRestrictionHours: integer('sameSongRestrictionHours'),
+ sameArtistRestrictionHours: integer('sameArtistRestrictionHours'),
cardCodeEnabled: boolean('cardCodeEnabled').default(false).notNull(),
cardCodeSubmissionRequired: boolean('cardCodeSubmissionRequired').default(false).notNull(),
cardCodeVoteEnabled: boolean('cardCodeVoteEnabled').default(false).notNull(),
cardCodeReplayEnabled: boolean('cardCodeReplayEnabled').default(false).notNull(),
+ enableCardCodeRequests: boolean('enableCardCodeRequests').default(false).notNull(),
+ requireCardCodeForRequests: boolean('requireCardCodeForRequests').default(false).notNull(),
+ enableCardCodeLimitBypass: boolean('enableCardCodeLimitBypass').default(false).notNull(),
submissionRestrictionEnabled: boolean('submissionRestrictionEnabled').default(false).notNull(),
submissionRestrictionGradeEnabled: boolean('submissionRestrictionGradeEnabled').default(false).notNull(),
submissionRestrictionClassEnabled: boolean('submissionRestrictionClassEnabled').default(false).notNull(),
@@ -282,16 +302,56 @@ export const systemSettings = pgTable('SystemSettings', {
registerRequireEmailVerify: boolean('registerRequireEmailVerify').default(false).notNull(),
registerDefaultRole: text('registerDefaultRole').default('USER'),
registerEnabledGradeClasses: text('registerEnabledGradeClasses'),
+ registerRequiresApproval: boolean('registerRequiresApproval').default(false).notNull(),
+ oauthRegisterRequiresApproval: boolean('oauthRegisterRequiresApproval').default(false).notNull(),
+ registerEmailRequired: boolean('registerEmailRequired').default(false).notNull(),
+ registerRequiresGradeClass: boolean('registerRequiresGradeClass').default(false).notNull(),
oauthEnabled: boolean('oauthEnabled').default(false).notNull(),
oauthAllowRegister: boolean('oauthAllowRegister').default(true).notNull(),
oauthAllowLogin: boolean('oauthAllowLogin').default(true).notNull(),
oauthAllowBind: boolean('oauthAllowBind').default(true).notNull(),
+ allowOAuthRegistration: boolean('allowOAuthRegistration').default(false).notNull(),
+ oauthRedirectUri: text('oauthRedirectUri'),
+ oauthStateSecret: text('oauthStateSecret'),
+ oauthProviders: text('oauthProviders').default('[]'),
+ githubOAuthEnabled: boolean('githubOAuthEnabled').default(false).notNull(),
+ githubClientId: text('githubClientId'),
+ githubClientSecret: text('githubClientSecret'),
+ casdoorOAuthEnabled: boolean('casdoorOAuthEnabled').default(false).notNull(),
+ casdoorServerUrl: text('casdoorServerUrl'),
+ casdoorClientId: text('casdoorClientId'),
+ casdoorClientSecret: text('casdoorClientSecret'),
+ casdoorOrganizationName: text('casdoorOrganizationName'),
+ googleOAuthEnabled: boolean('googleOAuthEnabled').default(false).notNull(),
+ googleClientId: text('googleClientId'),
+ googleClientSecret: text('googleClientSecret'),
+ aggregateOAuthEnabled: boolean('aggregateOAuthEnabled').default(false).notNull(),
+ aggregateOAuthAppId: text('aggregateOAuthAppId'),
+ aggregateOAuthAppKey: text('aggregateOAuthAppKey'),
+ aggregateOAuthLoginType: text('aggregateOAuthLoginType').default('qq'),
+ aggregateOAuthEndpoint: text('aggregateOAuthEndpoint'),
+ customOAuthEnabled: boolean('customOAuthEnabled').default(false).notNull(),
+ customOAuthDisplayName: text('customOAuthDisplayName'),
+ customOAuthAuthorizeUrl: text('customOAuthAuthorizeUrl'),
+ customOAuthTokenUrl: text('customOAuthTokenUrl'),
+ customOAuthUserInfoUrl: text('customOAuthUserInfoUrl'),
+ customOAuthScope: text('customOAuthScope'),
+ customOAuthClientId: text('customOAuthClientId'),
+ customOAuthClientSecret: text('customOAuthClientSecret'),
+ customOAuthUserIdField: text('customOAuthUserIdField'),
+ customOAuthUsernameField: text('customOAuthUsernameField'),
+ customOAuthNameField: text('customOAuthNameField'),
+ customOAuthEmailField: text('customOAuthEmailField'),
+ customOAuthAvatarField: text('customOAuthAvatarField'),
+ captchaMaxFailures: integer('captchaMaxFailures').default(3).notNull(),
autoBackupEnabled: boolean('autoBackupEnabled').default(false).notNull(),
autoBackupIntervalHours: integer('autoBackupIntervalHours').default(24).notNull(),
autoBackupRetentionDays: integer('autoBackupRetentionDays').default(30).notNull(),
+ autoBackupConfig: text('autoBackupConfig'),
statisticsCode: text('statisticsCode'),
- defaultTheme: text('defaultTheme').default('light'),
- enabledThemes: text('enabledThemes').default('light,dark'),
+ statisticsCodeEnabled: boolean('statisticsCodeEnabled').default(false).notNull(),
+ defaultTheme: text('defaultTheme').default('System').notNull(),
+ enabledThemes: text('enabledThemes').default('[\"System\",\"ClassicDark\",\"ClassicLight\",\"ModernLight\"]').notNull(),
enabledPlatforms: text('enabledPlatforms'),
platformOrder: text('platformOrder'),
});
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/nuxt.config.ts b/nuxt.config.ts
index a141ae2..c20d613 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',
@@ -150,6 +190,10 @@ export default defineNuxtConfig({
},
// 移动端优化
{ name: 'theme-color', content: '#111111' },
+ {
+ name: 'x-default-theme',
+ content: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'System'
+ },
{ 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='System'}`,
` }`,
- ` 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 e58e6e4..6453acf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,8 @@
"@pixi/filter-bulge-pinch": "^5.1.1",
"@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3",
+ "@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",
@@ -35,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",
@@ -77,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",
@@ -420,6 +424,7 @@
"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",
@@ -537,6 +542,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",
@@ -610,6 +657,23 @@
"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",
@@ -667,6 +731,20 @@
"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",
@@ -695,6 +773,143 @@
"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",
@@ -715,6 +930,765 @@
"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",
+ "peer": true,
+ "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/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz",
+ "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==",
+ "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-reserved-words": {
+ "version": "7.29.7",
+ "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/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.29.7",
+ "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/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-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-sticky-regex": {
+ "version": "7.29.7",
+ "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/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.29.7",
+ "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/plugin-transform-typeof-symbol": {
+ "version": "7.29.7",
+ "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"
},
@@ -744,6 +1718,177 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.29.7",
+ "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/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "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/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-unicode-regex": {
+ "version": "7.29.7",
+ "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": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.29.7",
+ "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",
+ "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/preset-env": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.7.tgz",
+ "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-plugin-utils": "^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"
+ },
+ "peerDependencies": {
+ "@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",
@@ -805,31 +1950,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": {
@@ -971,6 +2109,17 @@
"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",
@@ -1839,7 +2988,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.4.3"
@@ -1858,7 +3007,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==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1871,7 +3020,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -1915,7 +3064,7 @@
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
@@ -1930,14 +3079,14 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
+ "devOptional": 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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -1948,7 +3097,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -1961,7 +3110,7 @@
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0"
@@ -2032,7 +3181,7 @@
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
@@ -2066,7 +3215,7 @@
"version": "3.3.7",
"resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.7.tgz",
"integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.14.0",
@@ -2090,14 +3239,14 @@
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
+ "devOptional": 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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -2108,7 +3257,7 @@
"version": "14.0.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -2121,7 +3270,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -2134,7 +3283,7 @@
"version": "9.39.5",
"resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.5.tgz",
"integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2147,7 +3296,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==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2157,7 +3306,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==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0",
@@ -2177,7 +3326,7 @@
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
@@ -2187,7 +3336,7 @@
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
@@ -2201,7 +3350,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.22"
@@ -2215,7 +3364,7 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18"
@@ -2686,11 +3835,40 @@
}
}
},
+ "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"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@nuxt/devalue": {
"version": "2.0.2",
@@ -2783,6 +3961,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",
@@ -2881,6 +4087,36 @@
}
}
},
+ "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": {
"version": "10.0.1",
"resolved": "https://registry.npmmirror.com/@eslint/js/-/js-10.0.1.tgz",
@@ -3002,6 +4238,7 @@
"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",
@@ -3163,6 +4400,7 @@
"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",
@@ -3341,6 +4579,123 @@
"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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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",
@@ -3396,6 +4751,7 @@
"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"
}
@@ -3409,41 +4765,6 @@
"@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",
@@ -3874,6 +5195,7 @@
"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"
@@ -3905,6 +5227,7 @@
"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",
@@ -3925,6 +5248,7 @@
"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"
}
@@ -3940,6 +5264,7 @@
"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"
}
@@ -3949,6 +5274,7 @@
"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"
}
@@ -3958,6 +5284,7 @@
"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"
}
@@ -3990,6 +5317,7 @@
"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"
@@ -4089,6 +5417,7 @@
"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"
},
@@ -4416,6 +5745,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",
@@ -4915,6 +6270,212 @@
"win32"
]
},
+ "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",
+ "dependencies": {
+ "@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"
+ },
+ "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": ">=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/@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": {
+ "@sentry/conventions": "^0.16.0"
+ },
+ "engines": {
+ "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": {
+ "@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": {
+ "@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/@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",
+ "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": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz",
@@ -5011,6 +6572,7 @@
"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",
@@ -5235,6 +6797,7 @@
"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"
}
@@ -5254,6 +6817,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",
@@ -5341,8 +6919,9 @@
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
- "license": "MIT"
+ "devOptional": true,
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.10",
@@ -5413,6 +6992,7 @@
"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": "*",
@@ -5467,8 +7047,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",
@@ -5524,6 +7103,7 @@
"integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/types": "8.69.0",
@@ -6232,6 +7812,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",
@@ -6373,6 +8029,7 @@
"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",
@@ -6534,6 +8191,7 @@
"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"
},
@@ -6554,7 +8212,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -6582,7 +8240,7 @@
"version": "6.15.0",
"resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz",
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -6855,9 +8513,46 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
+ "devOptional": 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",
@@ -6917,6 +8612,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",
@@ -6974,6 +8678,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",
@@ -7025,6 +8744,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",
@@ -7039,6 +8806,7 @@
"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": "*"
},
@@ -7247,6 +9015,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.11.20",
"caniuse-lite": "^1.0.30001810",
@@ -7423,6 +9192,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",
@@ -7456,7 +9243,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -7632,6 +9419,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",
@@ -7720,6 +9513,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",
@@ -7909,7 +9711,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"
@@ -8026,6 +9827,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",
@@ -8209,6 +10019,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",
@@ -8291,7 +10152,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/deepmerge": {
@@ -8331,6 +10192,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",
@@ -8343,6 +10221,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",
@@ -8465,6 +10360,7 @@
"integrity": "sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==",
"dev": true,
"license": "MIT",
+ "peer": true,
"peerDependencies": {
"srvx": ">=0.11.5"
},
@@ -8509,6 +10405,7 @@
"integrity": "sha512-aOuk+yNJA61yA6eJJvQJxpJLIxz090hKMqDGzd35tGsvwaodi1RPuYSFUp5EINaJp7snjVwLkO2RXVMPZBYnnA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"srvx": "bin/srvx.mjs"
},
@@ -9137,6 +11034,7 @@
"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",
@@ -9304,6 +11202,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",
@@ -9352,6 +11265,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",
@@ -9377,9 +11376,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"
@@ -9403,12 +11402,33 @@
"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"
},
@@ -9463,7 +11483,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -9477,8 +11497,9 @@
"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.",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -9833,7 +11854,7 @@
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
@@ -9867,7 +11888,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==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9880,14 +11901,14 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
+ "devOptional": 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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -9898,7 +11919,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
@@ -9915,7 +11936,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
@@ -9932,7 +11953,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
@@ -9948,7 +11969,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -9961,7 +11982,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
@@ -9977,7 +11998,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
@@ -9993,7 +12014,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -10006,7 +12027,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -10019,7 +12040,7 @@
"version": "10.4.0",
"resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.15.0",
@@ -10037,7 +12058,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-3-Clause",
"dependencies": {
"estraverse": "^5.1.0"
@@ -10050,7 +12071,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
@@ -10063,7 +12084,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -10079,12 +12100,23 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/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",
@@ -10160,7 +12192,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==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-fifo": {
@@ -10201,14 +12232,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==",
- "dev": 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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/fast-npm-meta": {
@@ -10252,6 +12282,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",
@@ -10297,7 +12343,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"flat-cache": "^4.0.0"
@@ -10312,6 +12358,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",
@@ -10358,7 +12440,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
@@ -10372,7 +12454,7 @@
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
"integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
- "dev": true,
+ "devOptional": true,
"license": "ISC"
},
"node_modules/fnv1a-64": {
@@ -10401,6 +12483,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",
@@ -10538,6 +12635,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",
@@ -10629,6 +12758,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",
@@ -10660,6 +12795,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",
@@ -10744,6 +12896,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",
@@ -10836,6 +13004,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",
@@ -10845,6 +13025,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",
@@ -11057,6 +13264,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",
@@ -11124,7 +13337,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -11137,6 +13350,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",
@@ -11154,7 +13381,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=0.8.19"
@@ -11199,6 +13426,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",
@@ -11236,6 +13477,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",
@@ -11248,6 +13540,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",
@@ -11264,6 +13572,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",
@@ -11279,6 +13599,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",
@@ -11294,6 +13647,21 @@
"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",
@@ -11303,6 +13671,21 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -11406,12 +13789,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",
@@ -11421,6 +13828,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",
@@ -11460,6 +13892,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",
@@ -11472,6 +13940,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",
@@ -11520,6 +14079,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",
@@ -11539,7 +14115,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==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "github",
@@ -11584,7 +14160,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/json-schema-to-typescript-lite": {
@@ -11602,14 +14178,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/json5": {
@@ -11636,6 +14212,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",
@@ -11730,7 +14315,7 @@
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -11981,11 +14566,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1",
@@ -12009,6 +14603,7 @@
"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"
},
@@ -12381,6 +14976,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",
@@ -12421,7 +15022,7 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/lodash.once": {
@@ -12709,6 +15310,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",
@@ -12825,7 +15432,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/negotiator": {
@@ -13596,6 +16203,7 @@
"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",
@@ -13734,6 +16342,7 @@
"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"
}
@@ -13900,6 +16509,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",
@@ -14019,8 +16657,9 @@
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@@ -14044,6 +16683,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",
@@ -14159,7 +16816,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -14393,6 +17050,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",
@@ -14412,6 +17078,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"nanoid": "^3.3.18",
"picocolors": "^1.1.1",
@@ -14957,6 +17624,7 @@
"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"
@@ -15007,6 +17675,7 @@
"resolved": "https://registry.npmmirror.com/postgres/-/postgres-3.4.9.tgz",
"integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==",
"license": "Unlicense",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -15070,7 +17739,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 0.8.0"
@@ -15162,7 +17831,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -15448,6 +18117,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",
@@ -15469,11 +18178,53 @@
"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"
@@ -15652,6 +18403,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",
@@ -15695,7 +18468,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -15802,6 +18575,7 @@
"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"
@@ -16148,6 +18922,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",
@@ -16168,6 +18967,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",
@@ -16378,6 +19199,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",
@@ -16424,14 +19291,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"
},
@@ -16443,13 +19310,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"
@@ -16698,6 +19565,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",
@@ -16761,6 +19641,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",
@@ -16786,6 +19764,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",
@@ -16815,7 +19802,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==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -17073,6 +20060,7 @@
"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",
@@ -17173,6 +20161,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",
@@ -17399,6 +20438,7 @@
"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"
@@ -17435,7 +20475,7 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1"
@@ -17472,6 +20512,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",
@@ -17482,8 +20596,9 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -17504,6 +20619,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",
@@ -17575,6 +20708,7 @@
"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"
@@ -17591,6 +20725,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",
@@ -17670,6 +20844,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",
@@ -17778,6 +20964,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"napi-postinstall": "^0.3.4"
},
@@ -17981,6 +21168,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",
@@ -18021,7 +21218,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
@@ -18107,6 +21304,7 @@
"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",
@@ -18403,6 +21601,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",
@@ -18470,6 +21710,7 @@
"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",
@@ -18513,6 +21754,7 @@
"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",
@@ -18536,6 +21778,7 @@
"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"
},
@@ -18602,12 +21845,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",
@@ -18630,12 +21964,310 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "dev": true,
+ "devOptional": 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",
+ "peer": true,
+ "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",
@@ -18823,8 +22455,9 @@
"version": "2.9.0",
"resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz",
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "dev": true,
+ "devOptional": true,
"license": "ISC",
+ "peer": true,
"bin": {
"yaml": "bin.mjs"
},
diff --git a/package.json b/package.json
index 080b1d2..cf0977c 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,8 @@
"@pixi/filter-bulge-pinch": "^5.1.1",
"@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3",
+ "@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",
@@ -60,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",
@@ -105,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/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/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' })
// 自动判断是否需要secure
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)
+
// 优先从Redis缓存获取用户认证状态
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/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))
+ }
+
// 优先从普通API缓存获取数据
// 尝试从CacheService获取排期数据
@@ -49,6 +75,7 @@ export default defineEventHandler(async (event) => {
if (semester) {
filteredSchedules = cachedSchedules.filter((s) => s.song?.semester === semester)
}
+ filteredSchedules = applyVisibilityFilter(filteredSchedules)
// 转换为开放API格式
const result = {
@@ -105,11 +132,12 @@ export default defineEventHandler(async (event) => {
})
if (redisData) {
+ const filteredRedisData = applyVisibilityFilter(redisData)
// 转换为开放API格式
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/site-config.get.ts b/server/api/site-config.get.ts
index 8ead517..2e0c55c 100644
--- a/server/api/site-config.get.ts
+++ b/server/api/site-config.get.ts
@@ -26,7 +26,13 @@ const publicFields = [
'enableSubmissionRemarks',
'enableRequestTimeLimitation',
'forceBlockAllRequests',
- 'smtpEnabled'
+ 'smtpEnabled',
+ 'defaultTheme',
+ 'enabledThemes',
+ 'scheduleDaysBeforeEnabled',
+ 'scheduleDaysBefore',
+ 'scheduleDaysAfterEnabled',
+ 'scheduleDaysAfter'
]
const filterPublicSettings = (data: any) => {
@@ -81,7 +87,13 @@ export default defineEventHandler(async (event) => {
enableRequestTimeLimitation: false,
forceBlockAllRequests: false,
enableReplayRequests: false,
- smtpEnabled: false
+ smtpEnabled: false,
+ defaultTheme: 'System',
+ enabledThemes: '["System","ClassicDark","ClassicLight","ModernLight"]',
+ scheduleDaysBeforeEnabled: false,
+ scheduleDaysBefore: 30,
+ scheduleDaysAfterEnabled: false,
+ scheduleDaysAfter: 14
})
.returning()
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/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..abf422f 100644
--- a/server/api/songs/request.post.ts
+++ b/server/api/songs/request.post.ts
@@ -4,14 +4,16 @@ 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'
export default defineEventHandler(async (event) => {
// 检查用户认证
@@ -132,6 +134,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 +257,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 }[] = []
@@ -367,6 +407,7 @@ export default defineEventHandler(async (event) => {
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/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: '缺少有效点歌券ID' })
+ }
+ 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('开放API删除点歌券失败', 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> | null = null
+ try {
+ client = getRedisClient()
+ if (!client) return
+ const ttlSeconds = Math.ceil(EMAIL_CODE_TTL_MS / 1000)
+ await client.set(codeRedisKey(email), code, { EX: ttlSeconds })
+ await client.set(cooldownRedisKey(email), '1', {
+ EX: Math.ceil(EMAIL_CODE_COOLDOWN_MS / 1000)
+ })
+ const dailyKey = dailyRedisKey(email)
+ const dailyCount = await client.incr(dailyKey)
+ if (dailyCount === 1) {
+ await client.expire(dailyKey, Math.ceil(EMAIL_CODE_DAILY_WINDOW_MS / 1000))
+ }
+ } catch (error) {
+ console.error('[EmailCode] Redis 写入失败,已回退内存存储:', error)
+ }
+}
+
+export async function isEmailCodeCooldownActive(email: string): Promise {
+ let client: Awaited> | null = null
+ try {
+ client = getRedisClient()
+ if (client) {
+ return Boolean(await client.exists(cooldownRedisKey(email)))
+ }
+ } catch (error) {
+ console.error('[EmailCode] Redis 读取失败,已回退内存判断:', error)
+ }
+ const entry = emailCodes.get(normalizeEmail(email))
+ return Boolean(entry && getServerTimestamp() - entry.createdAt < EMAIL_CODE_COOLDOWN_MS)
+}
+
+export async function isEmailCodeDailyLimitReached(email: string): Promise {
+ let client: Awaited> | null = null
+ try {
+ client = getRedisClient()
+ if (client) {
+ const count = await client.get(dailyRedisKey(email))
+ return Number(count || 0) >= EMAIL_CODE_DAILY_LIMIT
+ }
+ } catch (error) {
+ console.error('[EmailCode] Redis 读取失败,已回退内存判断:', error)
+ }
+ const normalized = normalizeEmail(email)
+ const dayKey = getDayKey()
+ const record = dailySendCounts.get(normalized)
+ return Boolean(record && record.dayKey === dayKey && record.count >= EMAIL_CODE_DAILY_LIMIT)
+}
+
+export async function verifyEmailCode(email: string, code: string): Promise {
+ const trimmedCode = code.trim()
+ let client
+ try {
+ client = getRedisClient()
+ if (client) {
+ const result = await client.eval(VERIFY_EMAIL_CODE_SCRIPT, {
+ keys: [codeRedisKey(email), attemptsRedisKey(email)],
+ arguments: [trimmedCode, String(EMAIL_CODE_MAX_ATTEMPTS)]
+ })
+ if (Array.isArray(result)) {
+ return Number(result[0]) === 1
+ }
+ }
+ } catch (error) {
+ console.error('[EmailCode] Redis 校验失败,已回退内存校验:', error)
+ }
+
+ const normalized = normalizeEmail(email)
+ const entry = emailCodes.get(normalized)
+ if (!entry) return false
+ if (getServerTimestamp() > entry.expiresAt) {
+ emailCodes.delete(normalized)
+ return false
+ }
+ if (safeCodeEqual(entry.code, trimmedCode)) {
+ emailCodes.delete(normalized)
+ return true
+ }
+ entry.attempts++
+ if (entry.attempts >= EMAIL_CODE_MAX_ATTEMPTS) {
+ emailCodes.delete(normalized)
+ }
+ return false
+}
+
+export function cleanupExpiredEmailCodes(): void {
+ const now = getServerTimestamp()
+ for (const [email, entry] of emailCodes) {
+ if (now > entry.expiresAt) emailCodes.delete(email)
+ }
+}
diff --git a/server/utils/grade-class-core.ts b/server/utils/grade-class-core.ts
new file mode 100644
index 0000000..39234a5
--- /dev/null
+++ b/server/utils/grade-class-core.ts
@@ -0,0 +1,36 @@
+export const smartSort = (a: string, b: string) => {
+ const gradeOrder: Record = {
+ '初一': 1, '初二': 2, '初三': 3,
+ '高一': 4, '高二': 5, '高三': 6,
+ '大一': 7, '大二': 8, '大三': 9, '大四': 10,
+ '教师': 99, '教职工': 99
+ }
+
+ const weightA = gradeOrder[a]
+ const weightB = gradeOrder[b]
+
+ if (weightA !== undefined && weightB !== undefined) return weightA - weightB
+ if (weightA !== undefined) return -1
+ if (weightB !== undefined) return 1
+
+ return a.localeCompare(b, 'zh-CN', { numeric: true })
+}
+
+export interface GradeClassOption {
+ grade: string
+ class: string
+}
+
+export const resolveGradeClassOptions = (
+ configItems: GradeClassOption[],
+ fallbackItems: GradeClassOption[]
+): GradeClassOption[] => {
+ const source = configItems.length > 0 ? configItems : fallbackItems
+
+ return source
+ .filter((item): item is GradeClassOption => Boolean(item.grade?.trim()) && Boolean(item.class?.trim()))
+ .sort((a, b) => {
+ const gradeResult = smartSort(a.grade, b.grade)
+ return gradeResult || smartSort(a.class, b.class)
+ })
+}
diff --git a/server/utils/grade-class-options.ts b/server/utils/grade-class-options.ts
new file mode 100644
index 0000000..b7fc19b
--- /dev/null
+++ b/server/utils/grade-class-options.ts
@@ -0,0 +1,45 @@
+import { and, isNotNull, eq, ne, sql } from 'drizzle-orm'
+import { db } from '~/drizzle/db'
+import { users, gradeClasses } from '~/drizzle/schema'
+import { resolveGradeClassOptions, type GradeClassOption } from './grade-class-core'
+
+export async function fetchGradeClassOptions(): Promise {
+ const [configRows, userRows] = await Promise.all([
+ db.select({
+ grade: gradeClasses.grade,
+ class: gradeClasses.class
+ }).from(gradeClasses),
+ db
+ .selectDistinct({
+ grade: users.grade,
+ class: users.class
+ })
+ .from(users)
+ .where(and(eq(users.status, 'active'), isNotNull(users.grade), isNotNull(users.class)))
+ ])
+
+ return resolveGradeClassOptions(configRows, userRows)
+}
+
+export async function isGradeClassValid(grade: string, studentClass: string): Promise {
+ const configHit = await db.query.gradeClasses.findFirst({
+ where: (t, { eq: eq_, and: and_ }) => and_(eq_(t.grade, grade), eq_(t.class, studentClass)),
+ columns: { id: true }
+ })
+
+ if (configHit) return true
+
+ const configCount = await db
+ .select({ count: () => sql`count(*)::int` })
+ .from(gradeClasses)
+ .where(and(ne(gradeClasses.grade, ''), ne(gradeClasses.class, '')))
+ if ((configCount[0]?.count || 0) > 0) return false
+
+ const userHit = await db.query.users.findFirst({
+ where: (t, { eq: eq_, and: and_ }) =>
+ and_(eq_(t.status, 'active'), eq_(t.grade, grade), eq_(t.class, studentClass)),
+ columns: { id: true }
+ })
+
+ return Boolean(userHit)
+}
diff --git a/server/utils/important-notification-policy.ts b/server/utils/important-notification-policy.ts
new file mode 100644
index 0000000..2c8c273
--- /dev/null
+++ b/server/utils/important-notification-policy.ts
@@ -0,0 +1,72 @@
+export const NOTIFICATION_TITLE_MAX_LENGTH = 200
+export const NOTIFICATION_CONTENT_MAX_LENGTH = 20000
+export const NOTIFICATION_SOURCES = {
+ SYSTEM: 'SYSTEM',
+ ADMIN_MANUAL: 'ADMIN_MANUAL'
+} as const
+
+export type NotificationSenderInput = {
+ id: number
+ name?: string | null
+ username?: string | null
+}
+
+export type NotificationSenderSnapshot = {
+ senderId: number | null
+ senderName: string | null
+ senderUsername: string | null
+}
+
+export const resolveNotificationSource = (sender: NotificationSenderInput | null) =>
+ sender ? NOTIFICATION_SOURCES.ADMIN_MANUAL : NOTIFICATION_SOURCES.SYSTEM
+
+export const createNotificationSenderSnapshot = (
+ sender: NotificationSenderInput | null = null
+): NotificationSenderSnapshot => ({
+ senderId: sender?.id ?? null,
+ senderName: sender?.name?.trim() || null,
+ senderUsername: sender?.username?.trim() || null
+})
+
+export const serializeNotificationSender = (snapshot: NotificationSenderSnapshot) =>
+ snapshot.senderId
+ ? {
+ id: snapshot.senderId,
+ name: snapshot.senderName,
+ username: snapshot.senderUsername
+ }
+ : null
+
+export const shouldCheckImportantNotification = (
+ authenticated: boolean,
+ userId?: number | null,
+ requirePasswordChange = false
+): boolean =>
+ authenticated && Number.isInteger(userId) && Number(userId) > 0 && !requirePasswordChange
+
+export const canSendSystemNotification = (role?: string): boolean =>
+ role === 'ADMIN' || role === 'SUPER_ADMIN'
+
+export const resolveImportantFlag = (value: unknown): boolean | null => {
+ if (value === undefined) return false
+ return typeof value === 'boolean' ? value : null
+}
+
+export const shouldDeliverSystemNotification = (
+ important: boolean,
+ notificationsEnabled?: boolean
+): boolean => important || notificationsEnabled !== false
+
+export const shouldRetainNotificationHistory = (type: string, source: string): boolean =>
+ type === 'SYSTEM_NOTICE' && source === NOTIFICATION_SOURCES.ADMIN_MANUAL
+
+export const createNotificationReadUpdate = (updatedAt = new Date()) => ({
+ read: true as const,
+ updatedAt
+})
+
+export const createNotificationUserDeleteUpdate = (updatedAt = new Date()) => ({
+ userDeleted: true as const,
+ read: true as const,
+ updatedAt
+})
diff --git a/server/utils/initial-password-policy.ts b/server/utils/initial-password-policy.ts
new file mode 100644
index 0000000..c1e379d
--- /dev/null
+++ b/server/utils/initial-password-policy.ts
@@ -0,0 +1,33 @@
+interface InitialPasswordUserState {
+ requirePasswordChange?: boolean | null
+}
+
+interface StoredPasswordState {
+ password?: string | null
+ passwordChangedAt?: Date | string | null
+}
+
+export interface PasswordSetupState {
+ hasSetPassword: boolean
+ needsInitialPasswordSetup: boolean
+}
+
+export function getPasswordSetupState(
+ storedPassword: StoredPasswordState,
+ requirePasswordChange: boolean
+): PasswordSetupState {
+ const hasSetPassword = !!storedPassword.password
+ return {
+ hasSetPassword,
+ needsInitialPasswordSetup:
+ !storedPassword.passwordChangedAt && (requirePasswordChange || !hasSetPassword)
+ }
+}
+
+export function canSetInitialPassword(
+ user: InitialPasswordUserState,
+ storedPassword: StoredPasswordState
+): boolean {
+ return getPasswordSetupState(storedPassword, !!user.requirePasswordChange)
+ .needsInitialPasswordSetup
+}
diff --git a/server/utils/instance-id.ts b/server/utils/instance-id.ts
new file mode 100644
index 0000000..2d7ef39
--- /dev/null
+++ b/server/utils/instance-id.ts
@@ -0,0 +1,112 @@
+import { randomUUID } from 'node:crypto'
+import { asc, eq, sql } from 'drizzle-orm'
+import { db } from '~/drizzle/db'
+import { systemSettings } from '~/drizzle/schema'
+import { SYSTEM_SETTINGS_DEFAULTS } from './system-settings-defaults'
+import { getServerTimestamp } from './serverTime'
+
+export interface InstanceIdInfo {
+ instanceId: string
+ persisted: boolean
+}
+
+let cachedInstanceIdInfo: InstanceIdInfo | null = null
+let cachedInstanceIdExpiresAt = 0
+let pendingInstanceIdPromise: Promise | null = null
+let pendingInstanceIdInfoPromise: Promise | null = null
+
+const TEMPORARY_INSTANCE_ID_CACHE_TTL_MS = 5 * 60 * 1000
+
+const loadOrCreateInstanceId = async (): Promise => {
+ const settingsResult = await db
+ .select({ id: systemSettings.id, instanceId: systemSettings.instanceId })
+ .from(systemSettings)
+ .orderBy(asc(systemSettings.id))
+ .limit(1)
+
+ const settings = settingsResult[0]
+
+ if (settings?.instanceId) {
+ return settings.instanceId
+ }
+
+ const instanceId = randomUUID()
+
+ if (!settings) {
+ await db
+ .insert(systemSettings)
+ .values({ id: 1, ...SYSTEM_SETTINGS_DEFAULTS, instanceId })
+ .onConflictDoUpdate({
+ target: systemSettings.id,
+ set: { instanceId, updatedAt: new Date() }
+ })
+
+ await db.execute(sql`
+ SELECT setval(pg_get_serial_sequence('"SystemSettings"', 'id'), (SELECT MAX(id) FROM "SystemSettings"))
+ `)
+ } else {
+ await db
+ .update(systemSettings)
+ .set({ instanceId, updatedAt: new Date() })
+ .where(eq(systemSettings.id, settings.id))
+ }
+
+ return instanceId
+}
+
+const getCachedInstanceIdInfo = (): InstanceIdInfo | null => {
+ if (!cachedInstanceIdInfo) {
+ return null
+ }
+
+ if (cachedInstanceIdInfo.persisted || cachedInstanceIdExpiresAt > getServerTimestamp()) {
+ return cachedInstanceIdInfo
+ }
+
+ return null
+}
+
+export const getInstanceIdInfo = async (): Promise => {
+ const cached = getCachedInstanceIdInfo()
+ if (cached) {
+ return cached
+ }
+
+ if (!pendingInstanceIdInfoPromise) {
+ pendingInstanceIdInfoPromise = loadOrCreateInstanceId()
+ .then((instanceId) => {
+ const info = { instanceId, persisted: true }
+ cachedInstanceIdInfo = info
+ cachedInstanceIdExpiresAt = Number.POSITIVE_INFINITY
+ return info
+ })
+ .catch((error) => {
+ const fallbackInstanceId = cachedInstanceIdInfo?.persisted === false
+ ? cachedInstanceIdInfo.instanceId
+ : randomUUID()
+
+ const info = { instanceId: fallbackInstanceId, persisted: false }
+ cachedInstanceIdInfo = info
+ cachedInstanceIdExpiresAt = getServerTimestamp() + TEMPORARY_INSTANCE_ID_CACHE_TTL_MS
+ console.warn('[Instance ID] Failed to persist instance ID, using temporary fallback value:', error)
+ return info
+ })
+ .finally(() => {
+ pendingInstanceIdInfoPromise = null
+ })
+ }
+
+ return pendingInstanceIdInfoPromise
+}
+
+export const getInstanceId = async (): Promise => {
+ if (!pendingInstanceIdPromise) {
+ pendingInstanceIdPromise = getInstanceIdInfo()
+ .then(({ instanceId }) => instanceId)
+ .finally(() => {
+ pendingInstanceIdPromise = null
+ })
+ }
+
+ return pendingInstanceIdPromise
+}
diff --git a/server/utils/jwt-enhanced.ts b/server/utils/jwt-enhanced.ts
index b57e83a..53aaa08 100644
--- a/server/utils/jwt-enhanced.ts
+++ b/server/utils/jwt-enhanced.ts
@@ -38,30 +38,34 @@ export class JWTEnhanced {
}
/**
- * 生成JWT token
+ * 生成JWT token(返回 token 字符串,兼容旧调用)
*/
static generateToken(userId: number, role: string): string {
+ return this.generateTokenWithJti(userId, role).token
+ }
+
+ /**
+ * 生成JWT token 并返回 jti(会话表关联需要)
+ */
+ static generateTokenWithJti(userId: number, role: string): { token: string; jti: string } {
if (!this.JWT_SECRET) {
throw new Error('JWT_SECRET environment variable is not set')
}
- // 生成唯一的JWT ID
const jti = `${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
- // JWT载荷
const payload: Omit = {
userId,
role,
jti
}
- // 生成token
const token = jwt.sign(payload, this.JWT_SECRET, {
expiresIn: this.TOKEN_EXPIRES
})
console.log(`Generated JWT token for user ${userId}`)
- return token
+ return { token, jti }
}
/**
diff --git a/server/utils/notification-history-policy.ts b/server/utils/notification-history-policy.ts
new file mode 100644
index 0000000..9457fe1
--- /dev/null
+++ b/server/utils/notification-history-policy.ts
@@ -0,0 +1,108 @@
+export const NOTIFICATION_HISTORY_STATUSES = ['ALL', 'READ', 'UNREAD'] as const
+
+export type NotificationHistoryStatus = (typeof NOTIFICATION_HISTORY_STATUSES)[number]
+
+export const NOTIFICATION_HISTORY_TYPES = ['ALL', 'IMPORTANT', 'NORMAL'] as const
+export const NOTIFICATION_HISTORY_SORT_ORDERS = ['DESC', 'ASC'] as const
+export const NOTIFICATION_HISTORY_KEYWORD_MAX_LENGTH = 100
+
+export type NotificationHistoryType = (typeof NOTIFICATION_HISTORY_TYPES)[number]
+export type NotificationHistorySortOrder = (typeof NOTIFICATION_HISTORY_SORT_ORDERS)[number]
+
+export const resolveNotificationHistoryFilters = (values: {
+ keyword?: unknown
+ type?: unknown
+ sender?: unknown
+ sortOrder?: unknown
+}): {
+ keyword: string
+ type: NotificationHistoryType
+ senderId: number | null
+ sortOrder: NotificationHistorySortOrder
+} | null => {
+ if (values.keyword !== undefined && typeof values.keyword !== 'string') return null
+
+ const keyword = typeof values.keyword === 'string' ? values.keyword.trim() : ''
+ if (keyword.length > NOTIFICATION_HISTORY_KEYWORD_MAX_LENGTH) return null
+
+ if (values.type !== undefined && typeof values.type !== 'string') return null
+ const type = typeof values.type === 'string' && values.type.trim()
+ ? values.type.trim().toUpperCase()
+ : 'ALL'
+ if (!NOTIFICATION_HISTORY_TYPES.includes(type as NotificationHistoryType)) return null
+
+ let senderId: number | null = null
+ if (values.sender !== undefined && values.sender !== null && values.sender !== '') {
+ if (typeof values.sender !== 'string') return null
+ const parsedSenderId = Number(values.sender.trim())
+ if (!Number.isInteger(parsedSenderId) || parsedSenderId <= 0) return null
+ senderId = parsedSenderId
+ }
+
+ if (values.sortOrder !== undefined && typeof values.sortOrder !== 'string') return null
+ const sortOrder = typeof values.sortOrder === 'string' && values.sortOrder.trim()
+ ? values.sortOrder.trim().toUpperCase()
+ : 'DESC'
+ if (!NOTIFICATION_HISTORY_SORT_ORDERS.includes(sortOrder as NotificationHistorySortOrder)) {
+ return null
+ }
+
+ return {
+ keyword,
+ type: type as NotificationHistoryType,
+ senderId,
+ sortOrder: sortOrder as NotificationHistorySortOrder
+ }
+}
+
+export const resolveNotificationHistoryStatus = (
+ value: unknown
+): NotificationHistoryStatus | null => {
+ if (value === undefined || value === null || value === '') return 'ALL'
+ if (typeof value !== 'string') return null
+
+ const normalized = value.trim().toUpperCase()
+ return NOTIFICATION_HISTORY_STATUSES.includes(normalized as NotificationHistoryStatus)
+ ? (normalized as NotificationHistoryStatus)
+ : null
+}
+
+export const resolveNotificationHistoryPagination = (
+ pageValue: unknown,
+ limitValue: unknown
+) => {
+ const parsedPage = Number(pageValue)
+ const parsedLimit = Number(limitValue)
+ const page = Number.isInteger(parsedPage) && parsedPage > 0 ? parsedPage : 1
+ const limit = Number.isInteger(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, 100) : 20
+
+ return {
+ page,
+ limit,
+ offset: (page - 1) * limit
+ }
+}
+
+const UUID_PATTERN =
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
+const LEGACY_BATCH_PATTERN = /^legacy-([1-9]\d*)$/
+
+export const resolveNotificationBatchReference = (value: unknown) => {
+ if (typeof value !== 'string') return null
+
+ const normalized = value.trim()
+ const legacyMatch = normalized.match(LEGACY_BATCH_PATTERN)
+ if (legacyMatch) {
+ return {
+ batchId: null,
+ notificationId: Number(legacyMatch[1])
+ }
+ }
+
+ if (!UUID_PATTERN.test(normalized)) return null
+
+ return {
+ batchId: normalized.toLowerCase(),
+ notificationId: null
+ }
+}
diff --git a/server/utils/oauth-identity.ts b/server/utils/oauth-identity.ts
new file mode 100644
index 0000000..1486f74
--- /dev/null
+++ b/server/utils/oauth-identity.ts
@@ -0,0 +1,85 @@
+import { db, eq, userIdentities, users } from '~/drizzle/db'
+import { getBeijingTime } from '~/utils/timeUtils'
+import { resolveAvatarSource } from '~~/server/utils/user-avatar'
+
+type DbTransaction = Parameters[0]>[0]
+
+export interface OAuthIdentityPayload {
+ provider: string
+ providerUserId: string
+ providerUsername?: string | null
+ avatar?: string | null
+}
+
+export interface ExistingOAuthIdentity {
+ id: number
+ avatar: string | null
+}
+
+export async function syncOAuthIdentityAvatar(
+ tx: DbTransaction,
+ user: { id: number; avatarProvider?: string | null; avatarProviderUserId?: string | null },
+ existing: ExistingOAuthIdentity | null | undefined,
+ payload: OAuthIdentityPayload
+): Promise<{ created: boolean; avatarSourceSet: boolean }> {
+ const currentIdentities = await tx.query.userIdentities.findMany({
+ where: (t, { eq: eqUserId }) => eqUserId(t.userId, user.id),
+ columns: {
+ id: true,
+ provider: true,
+ providerUserId: true,
+ providerUsername: true,
+ avatar: true,
+ createdAt: true
+ }
+ })
+ const currentAvatarSource = resolveAvatarSource(user, currentIdentities)
+
+ let created = false
+ if (existing) {
+ if (payload.avatar && existing.avatar !== payload.avatar) {
+ await tx
+ .update(userIdentities)
+ .set({ avatar: payload.avatar })
+ .where(eq(userIdentities.id, existing.id))
+ }
+ } else {
+ await tx.insert(userIdentities).values({
+ userId: user.id,
+ provider: payload.provider,
+ providerUserId: payload.providerUserId,
+ providerUsername: payload.providerUsername,
+ avatar: payload.avatar || null,
+ createdAt: getBeijingTime()
+ })
+ created = true
+ }
+
+ let avatarSourceSet = false
+ if (!currentAvatarSource) {
+ const updatedIdentities = await tx.query.userIdentities.findMany({
+ where: (t, { eq: eqUserId }) => eqUserId(t.userId, user.id),
+ columns: {
+ id: true,
+ provider: true,
+ providerUserId: true,
+ providerUsername: true,
+ avatar: true,
+ createdAt: true
+ }
+ })
+ const nextAvatarSource = resolveAvatarSource(user, updatedIdentities)
+ if (nextAvatarSource) {
+ await tx
+ .update(users)
+ .set({
+ avatarProvider: nextAvatarSource.provider,
+ avatarProviderUserId: nextAvatarSource.providerUserId
+ })
+ .where(eq(users.id, user.id))
+ avatarSourceSet = true
+ }
+ }
+
+ return { created, avatarSourceSet }
+}
diff --git a/server/utils/oauth-providers.ts b/server/utils/oauth-providers.ts
new file mode 100644
index 0000000..594754c
--- /dev/null
+++ b/server/utils/oauth-providers.ts
@@ -0,0 +1,126 @@
+export type OAuthProvider = 'github' | 'casdoor' | 'google' | 'oauth2' | 'aggregate'
+
+export const AGGREGATE_OAUTH_LOGIN_TYPES = [
+ 'qq',
+ 'wx',
+ 'alipay',
+ 'sina',
+ 'baidu',
+ 'douyin',
+ 'huawei',
+ 'xiaomi',
+ 'gitee',
+ 'gitea',
+ 'bilibili',
+ 'kuaishou'
+] as const
+
+export type AggregateOAuthLoginType = (typeof AGGREGATE_OAUTH_LOGIN_TYPES)[number]
+
+const isPrivateIPv4 = (hostname: string): boolean => {
+ const parts = hostname.split('.').map(Number)
+ if (
+ parts.length !== 4 ||
+ parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)
+ ) {
+ return false
+ }
+
+ const [first, second] = parts
+ return (
+ first === 10 ||
+ first === 127 ||
+ (first === 100 && second >= 64 && second <= 127) ||
+ (first === 169 && second === 254) ||
+ (first === 172 && second >= 16 && second <= 31) ||
+ (first === 192 && second === 168)
+ )
+}
+
+const isPrivateIPv6 = (hostname: string): boolean => {
+ const normalized = hostname.replace(/^\[|\]$/g, '').toLowerCase()
+ if (!normalized.includes(':')) return false
+ if (normalized.startsWith('::ffff:')) {
+ return isPrivateIPv4(normalized.slice('::ffff:'.length))
+ }
+ return (
+ normalized === '::1' ||
+ normalized.startsWith('fc') ||
+ normalized.startsWith('fd') ||
+ /^fe[89ab]/.test(normalized)
+ )
+}
+
+const isInternalHostname = (hostname: string): boolean => {
+ const normalized = hostname.toLowerCase()
+ if (normalized.includes(':')) return isPrivateIPv6(normalized)
+ if (isPrivateIPv4(normalized)) return true
+ return (
+ normalized === 'localhost' ||
+ !normalized.includes('.') ||
+ normalized.endsWith('.localhost') ||
+ normalized.endsWith('.local') ||
+ normalized.endsWith('.lan') ||
+ normalized.endsWith('.internal') ||
+ normalized.endsWith('.home.arpa')
+ )
+}
+
+export const isSafeAggregateOAuthUrl = (value: string): boolean => {
+ try {
+ const url = new URL(value)
+ if (url.username || url.password) return false
+ if (url.protocol === 'https:') return true
+ if (url.protocol !== 'http:') return false
+ return isInternalHostname(url.hostname)
+ } catch {
+ return false
+ }
+}
+
+export const normalizeAggregateOAuthLoginTypes = (value: unknown): AggregateOAuthLoginType[] => {
+ let values: unknown[] = []
+
+ if (Array.isArray(value)) {
+ values = value
+ } else if (typeof value === 'string' && value.trim()) {
+ const normalized = value.trim()
+ try {
+ const parsed = JSON.parse(normalized)
+ values = Array.isArray(parsed) ? parsed : [parsed]
+ } catch {
+ values = normalized.split(',')
+ }
+ }
+
+ return [
+ ...new Set(
+ values
+ .filter((item): item is string => typeof item === 'string')
+ .map((item) => item.trim().toLowerCase())
+ .filter((item): item is AggregateOAuthLoginType =>
+ AGGREGATE_OAUTH_LOGIN_TYPES.includes(item as AggregateOAuthLoginType)
+ )
+ )
+ ]
+}
+
+export const getAggregateOAuthLoginTypesOrDefault = (value: unknown): AggregateOAuthLoginType[] => {
+ const normalized = normalizeAggregateOAuthLoginTypes(value)
+ const hasConfiguredValue =
+ (Array.isArray(value) && value.length > 0) ||
+ (typeof value === 'string' && value.trim().length > 0)
+ return normalized.length > 0 || hasConfiguredValue ? normalized : ['qq']
+}
+
+export const SUPPORTED_OAUTH_PROVIDERS: OAuthProvider[] = [
+ 'github',
+ 'casdoor',
+ 'google',
+ 'oauth2',
+ 'aggregate'
+]
+
+export const isSupportedOAuthProvider = (provider: string): provider is OAuthProvider => {
+ return SUPPORTED_OAUTH_PROVIDERS.includes(provider as OAuthProvider)
+}
diff --git a/server/utils/oauth-strategies.ts b/server/utils/oauth-strategies.ts
index d317281..333c421 100644
--- a/server/utils/oauth-strategies.ts
+++ b/server/utils/oauth-strategies.ts
@@ -1,4 +1,12 @@
import { createError } from 'h3'
+import { createHmac, randomUUID } from 'node:crypto'
+import { db } from '~/drizzle/db'
+import { systemSettings } from '~/drizzle/schema'
+import { asc } from 'drizzle-orm'
+import {
+ isSafeAggregateOAuthUrl,
+ getAggregateOAuthLoginTypesOrDefault
+} from './oauth-providers'
export interface OAuthUserInfo {
id: string
@@ -250,6 +258,139 @@ const strategies: Record = {
google: googleStrategy
}
+async function loadAggregateSettings() {
+ const row = await db
+ .select({
+ aggregateOAuthEnabled: systemSettings.aggregateOAuthEnabled,
+ aggregateOAuthAppId: systemSettings.aggregateOAuthAppId,
+ aggregateOAuthAppKey: systemSettings.aggregateOAuthAppKey,
+ aggregateOAuthLoginType: systemSettings.aggregateOAuthLoginType,
+ aggregateOAuthEndpoint: systemSettings.aggregateOAuthEndpoint
+ })
+ .from(systemSettings)
+ .orderBy(asc(systemSettings.id))
+ .limit(1)
+ return row[0] ?? null
+}
+
+function normalizeEndpoint(endpoint: string): string {
+ return endpoint.replace(/\/+$/, '')
+}
+
+function aggregateSign(params: Record, appKey: string): string {
+ const sorted = Object.keys(params)
+ .filter((k) => params[k] !== undefined && params[k] !== null && params[k] !== '')
+ .sort()
+ .map((k) => `${k}=${params[k]}`)
+ .join('&')
+ return createHmac('sha256', appKey).update(sorted).digest('hex')
+}
+
+const aggregateStrategy: OAuthStrategy = {
+ async getAuthorizeUrl(redirectUri: string, state: string) {
+ const settings = await loadAggregateSettings()
+ if (!settings?.aggregateOAuthEnabled || !settings.aggregateOAuthAppId || !settings.aggregateOAuthAppKey || !settings.aggregateOAuthEndpoint) {
+ throw createError({ statusCode: 500, message: '聚合登录未配置' })
+ }
+ if (!isSafeAggregateOAuthUrl(settings.aggregateOAuthEndpoint)) {
+ throw createError({ statusCode: 500, message: '聚合登录端点不安全或格式错误' })
+ }
+
+ const loginTypes = getAggregateOAuthLoginTypesOrDefault(settings.aggregateOAuthLoginType)
+ const loginType = loginTypes[0] || 'qq'
+ const endpoint = normalizeEndpoint(settings.aggregateOAuthEndpoint)
+ const timestamp = String(Math.floor(Date.now() / 1000))
+ const nonce = randomUUID().replace(/-/g, '')
+ const baseParams: Record = {
+ app_id: settings.aggregateOAuthAppId,
+ response_type: 'code',
+ redirect_uri: redirectUri,
+ state,
+ login_type: loginType,
+ timestamp,
+ nonce
+ }
+ const sign = aggregateSign(baseParams, settings.aggregateOAuthAppKey)
+ const query = new URLSearchParams({ ...baseParams, sign }).toString()
+ return `${endpoint}/oauth/authorize?${query}`
+ },
+
+ async exchangeToken(code: string, redirectUri: string) {
+ const settings = await loadAggregateSettings()
+ if (!settings?.aggregateOAuthEnabled || !settings.aggregateOAuthAppId || !settings.aggregateOAuthAppKey || !settings.aggregateOAuthEndpoint) {
+ throw createError({ statusCode: 500, message: '聚合登录未配置' })
+ }
+ const endpoint = normalizeEndpoint(settings.aggregateOAuthEndpoint)
+ const timestamp = String(Math.floor(Date.now() / 1000))
+ const nonce = randomUUID().replace(/-/g, '')
+ const baseParams: Record = {
+ app_id: settings.aggregateOAuthAppId,
+ grant_type: 'authorization_code',
+ code,
+ redirect_uri: redirectUri,
+ timestamp,
+ nonce
+ }
+ const sign = aggregateSign(baseParams, settings.aggregateOAuthAppKey)
+
+ try {
+ const tokenResponse = await $fetch(`${endpoint}/oauth/token`, {
+ method: 'POST',
+ body: { ...baseParams, sign },
+ headers: { Accept: 'application/json' }
+ })
+ if (tokenResponse.error) {
+ throw new Error(tokenResponse.error_description || tokenResponse.error || '令牌请求失败')
+ }
+ const accessToken = tokenResponse.access_token || tokenResponse.accessToken
+ if (!accessToken) {
+ throw new Error('未能获取访问令牌')
+ }
+ return accessToken
+ } catch (e: any) {
+ console.error('Aggregate OAuth token exchange failed', e)
+ throw new Error(e.message || '令牌请求失败')
+ }
+ },
+
+ async getUserInfo(accessToken: string) {
+ const settings = await loadAggregateSettings()
+ if (!settings?.aggregateOAuthAppId || !settings?.aggregateOAuthAppKey || !settings?.aggregateOAuthEndpoint) {
+ throw createError({ statusCode: 500, message: '聚合登录未配置' })
+ }
+ const endpoint = normalizeEndpoint(settings.aggregateOAuthEndpoint)
+ const timestamp = String(Math.floor(Date.now() / 1000))
+ const nonce = randomUUID().replace(/-/g, '')
+ const baseParams: Record = {
+ app_id: settings.aggregateOAuthAppId,
+ access_token: accessToken,
+ timestamp,
+ nonce
+ }
+ const sign = aggregateSign(baseParams, settings.aggregateOAuthAppKey)
+
+ try {
+ const userInfo = await $fetch(`${endpoint}/oauth/userinfo`, {
+ method: 'POST',
+ body: { ...baseParams, sign },
+ headers: { Accept: 'application/json' }
+ })
+ return {
+ id: String(userInfo.id || userInfo.openid || userInfo.sub || userInfo.userId || ''),
+ username: userInfo.username || userInfo.nickname || userInfo.name || String(userInfo.id || 'user'),
+ name: userInfo.name || userInfo.nickname || undefined,
+ email: userInfo.email || undefined,
+ avatar: userInfo.avatar || userInfo.avatar_url || userInfo.headimgurl || undefined
+ }
+ } catch (e: any) {
+ console.error('Aggregate OAuth user info failed', e)
+ throw new Error('获取用户信息失败')
+ }
+ }
+}
+
+strategies.aggregate = aggregateStrategy
+
export const getOAuthStrategy = (provider: string): OAuthStrategy => {
const strategy = strategies[provider]
if (!strategy) {
diff --git a/server/utils/redis.ts b/server/utils/redis.ts
index c8bb351..f7f6ce7 100644
--- a/server/utils/redis.ts
+++ b/server/utils/redis.ts
@@ -261,6 +261,12 @@ export const disconnectRedis = () => redisPool.disconnect()
export const getRedisClient = () => redisPool.getClient()
export const isRedisReady = () => redisPool.isReady()
export const getRedisStats = () => redisPool.getStats()
+
+export const REDIS_KEY_PREFIX = 'voicehub'
+
+export function buildRedisKey(...segments: string[]): string {
+ return [REDIS_KEY_PREFIX, ...segments].filter(Boolean).join(':')
+}
export const executeRedisCommand = (command: () => Promise, fallback?: () => Promise) =>
redisPool.executeCommand(command, fallback)
diff --git a/server/utils/requireSongAdmin.ts b/server/utils/requireSongAdmin.ts
new file mode 100644
index 0000000..1a9eeba
--- /dev/null
+++ b/server/utils/requireSongAdmin.ts
@@ -0,0 +1,15 @@
+import { createApiError } from './apiError'
+import { SERVER_ERROR_CODES } from '../config/constants'
+
+const SONG_ADMIN_ROLES = ['SONG_ADMIN', 'ADMIN', 'SUPER_ADMIN']
+
+export function requireSongAdmin(event: any) {
+ const user = event.context?.user
+ if (!user) {
+ throw createApiError(401, SERVER_ERROR_CODES.AUTH_UNAUTHORIZED, '请先登录')
+ }
+ if (!SONG_ADMIN_ROLES.includes(user.role)) {
+ throw createApiError(403, SERVER_ERROR_CODES.AUTH_FORBIDDEN, '没有权限执行此操作')
+ }
+ return user
+}
diff --git a/server/utils/schedule-visibility.ts b/server/utils/schedule-visibility.ts
new file mode 100644
index 0000000..3412f33
--- /dev/null
+++ b/server/utils/schedule-visibility.ts
@@ -0,0 +1,52 @@
+export interface ScheduleVisibilityRange {
+ startAt: Date | null
+ endAt: Date | null
+}
+
+export interface VisibilitySettingsLike {
+ scheduleDaysBeforeEnabled?: boolean | null
+ scheduleDaysBefore?: number | null
+ scheduleDaysAfterEnabled?: boolean | null
+ scheduleDaysAfter?: number | null
+}
+
+export function resolveScheduleVisibilityRange(
+ settings: VisibilitySettingsLike | null | undefined,
+ referenceDate = new Date()
+): ScheduleVisibilityRange {
+ const today = new Date(
+ referenceDate.getFullYear(),
+ referenceDate.getMonth(),
+ referenceDate.getDate()
+ )
+
+ let startAt: Date | null = null
+ let endAt: Date | null = null
+
+ if (settings?.scheduleDaysBeforeEnabled) {
+ const days = Math.max(0, Number(settings.scheduleDaysBefore) || 0)
+ const d = new Date(today)
+ d.setDate(d.getDate() - days)
+ startAt = d
+ }
+
+ if (settings?.scheduleDaysAfterEnabled) {
+ const days = Math.max(0, Number(settings.scheduleDaysAfter) || 0)
+ const d = new Date(today)
+ d.setDate(d.getDate() + days + 1)
+ endAt = d
+ }
+
+ return { startAt, endAt }
+}
+
+export function isPlayDateInRange(
+ playDate: Date | string | null | undefined,
+ range: ScheduleVisibilityRange
+): boolean {
+ if (!playDate) return true
+ const d = new Date(playDate)
+ if (range.startAt && d.getTime() < range.startAt.getTime()) return false
+ if (range.endAt && d.getTime() >= range.endAt.getTime()) return false
+ return true
+}
diff --git a/server/utils/serverTime.ts b/server/utils/serverTime.ts
new file mode 100644
index 0000000..d8ac330
--- /dev/null
+++ b/server/utils/serverTime.ts
@@ -0,0 +1,3 @@
+export function getServerTimestamp(): number {
+ return Date.now()
+}
diff --git a/server/utils/submissionNoteAutoModeration.ts b/server/utils/submissionNoteAutoModeration.ts
new file mode 100644
index 0000000..ca3e29c
--- /dev/null
+++ b/server/utils/submissionNoteAutoModeration.ts
@@ -0,0 +1,150 @@
+import type { SongBlacklist } from '~/drizzle/schema'
+
+export type BlacklistType = 'KEYWORD' | 'LANGUAGE' | 'GENRE' | 'SONG'
+
+export interface ModerationContext {
+ title?: string | null
+ artist?: string | null
+ genre?: string | null
+}
+
+export interface ModerationResult {
+ notePublic: boolean
+ publicStatus: 'approved' | 'rejected' | null
+ rejectedReasons: string[]
+ matchedRules: { type: BlacklistType; value: string }[]
+}
+
+function escapeRegex(s: string): string {
+ return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+}
+
+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
+ const normalized = text.toLowerCase()
+ 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 function moderateSubmissionNote(
+ note: string | null,
+ wantPublic: boolean,
+ requiresApproval: boolean,
+ song: ModerationContext,
+ blacklistRows: SongBlacklist[]
+): ModerationResult {
+ if (!note || !wantPublic) {
+ return {
+ notePublic: wantPublic && !!note,
+ publicStatus: null,
+ rejectedReasons: [],
+ matchedRules: []
+ }
+ }
+
+ const noteLower = note.toLowerCase()
+ const noteForKeyword = note.toLowerCase()
+ const title = (song.title || '').toLowerCase()
+ const artist = (song.artist || '').toLowerCase()
+ const genre = (song.genre || '').toLowerCase()
+
+ const rejectedReasons: string[] = []
+ const matchedRules: { type: BlacklistType; value: string }[] = []
+
+ const noteLanguages = detectLanguages(note)
+ const textLanguages = new Set([
+ ...noteLanguages,
+ ...detectLanguages(song.title || ''),
+ ...detectLanguages(song.artist || ''),
+ ...detectLanguages(song.genre || '')
+ ])
+
+ for (const row of blacklistRows) {
+ const t = row.type as BlacklistType
+ const rawValue = row.value
+ if (!rawValue) continue
+ const value = rawValue.trim()
+ if (!value) continue
+ const valueLower = value.toLowerCase()
+
+ if (t === 'KEYWORD') {
+ // 命中 note / title / artist / genre 的其中一个都算
+ const haystacks = [noteForKeyword, title, artist, genre].filter(Boolean) as string[]
+ const exact = haystacks.some((h) => h.includes(valueLower))
+ const asRegex = (() => {
+ try {
+ const re = new RegExp(escapeRegex(value), 'i')
+ return haystacks.some((h) => re.test(h))
+ } catch (_) {
+ return false
+ }
+ })()
+ if (exact || asRegex) {
+ matchedRules.push({ type: t, value })
+ rejectedReasons.push(`命中敏感词:${value}`)
+ }
+ } else if (t === 'LANGUAGE') {
+ const v = valueLower
+ const checkList = [
+ v,
+ ...v.split(/[,,\/|\s]+/).map((s) => s.trim()).filter(Boolean)
+ ]
+ const hit = checkList.some((lang) => textLanguages.has(lang))
+ if (hit) {
+ matchedRules.push({ type: t, value })
+ rejectedReasons.push(`命中语言限制:${value}`)
+ }
+ } else if (t === 'GENRE') {
+ // 匹配流派:genre 字段或 note/title/artist 中的流派关键词
+ const combinedForGenre = `${genre} ${noteForKeyword} ${title} ${artist}`.trim()
+ if (!combinedForGenre) continue
+ const tokens = valueLower.split(/[,,\/|\s]+/).map((s) => s.trim()).filter(Boolean)
+ const hit = tokens.some((tok) => combinedForGenre.includes(tok))
+ if (hit) {
+ matchedRules.push({ type: t, value })
+ rejectedReasons.push(`命中流派限制:${value}`)
+ }
+ } else {
+ // SONG 不参与 note 审查
+ continue
+ }
+ }
+
+ const rejected = matchedRules.length > 0
+ return {
+ notePublic: rejected ? false : (requiresApproval ? false : wantPublic),
+ publicStatus: requiresApproval || matchedRules.length > 0 ? (rejected ? 'rejected' : 'approved') : null,
+ rejectedReasons,
+ matchedRules
+ }
+}
diff --git a/server/utils/system-settings-defaults.ts b/server/utils/system-settings-defaults.ts
new file mode 100644
index 0000000..7946b9e
--- /dev/null
+++ b/server/utils/system-settings-defaults.ts
@@ -0,0 +1,70 @@
+export const SYSTEM_SETTINGS_DEFAULTS = {
+ siteTitle: '校园广播站点歌系统',
+ siteVersion: '1.7.1.0',
+ enableSubmissionLimit: false,
+ showBlacklistKeywords: false,
+ hideStudentInfo: true,
+ smtpEnabled: false,
+ smtpPort: 587,
+ smtpSecure: false,
+ smtpFromName: '校园广播站',
+ enableRequestTimeLimitation: false,
+ forceBlockAllRequests: false,
+ enableReplayRequests: false,
+ enableCollaborativeSubmission: true,
+ enableSubmissionRemarks: false,
+ 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: 'System',
+ enabledThemes: '["System","ClassicDark","ClassicLight","ModernLight"]'
+} as const
diff --git a/server/utils/user-agent-info.ts b/server/utils/user-agent-info.ts
new file mode 100644
index 0000000..e33f3e9
--- /dev/null
+++ b/server/utils/user-agent-info.ts
@@ -0,0 +1,71 @@
+export interface UserAgentInfo {
+ browser: string | null
+ operatingSystem: string | null
+ device: string | null
+}
+
+const BROWSER_PATTERNS: Array<[RegExp, string]> = [
+ [/Edg\//i, 'Microsoft Edge'],
+ [/OPR\//i, 'Opera'],
+ [/Chrome\//i, 'Chrome'],
+ [/Safari\//i, 'Safari'],
+ [/Firefox\//i, 'Firefox'],
+ [/MSIE |Trident\//i, 'Internet Explorer']
+]
+
+const OS_PATTERNS: Array<[RegExp, string]> = [
+ [/Windows NT 10\.0/i, 'Windows 10/11'],
+ [/Windows NT 6\.3/i, 'Windows 8.1'],
+ [/Windows NT 6\.2/i, 'Windows 8'],
+ [/Windows NT 6\.1/i, 'Windows 7'],
+ [/Windows/i, 'Windows'],
+ [/Mac OS X ([\d_.]+)/i, 'macOS'],
+ [/iPhone OS ([\d_]+)/i, 'iOS'],
+ [/iPad.*CPU OS ([\d_]+)/i, 'iPadOS'],
+ [/Android ([\d.]+)/i, 'Android'],
+ [/Linux/i, 'Linux']
+]
+
+const DEVICE_PATTERNS: Array<[RegExp, string]> = [
+ [/iPhone/i, 'iPhone'],
+ [/iPad/i, 'iPad'],
+ [/Android.*Mobile/i, 'Android Phone'],
+ [/Android/i, 'Android Tablet'],
+ [/Windows Phone/i, 'Windows Phone'],
+ [/Macintosh/i, 'Mac'],
+ [/Windows/i, 'Windows PC'],
+ [/Linux/i, 'Linux PC']
+]
+
+export function parseUserAgent(userAgent: string | null | undefined): UserAgentInfo {
+ const ua = (userAgent ?? '').trim()
+ if (!ua) return { browser: null, operatingSystem: null, device: null }
+
+ let browser: string | null = null
+ for (const [regex, name] of BROWSER_PATTERNS) {
+ if (regex.test(ua)) {
+ browser = name
+ break
+ }
+ }
+
+ let operatingSystem: string | null = null
+ for (const [regex, name] of OS_PATTERNS) {
+ if (regex.test(ua)) {
+ operatingSystem = name
+ break
+ }
+ }
+
+ let device: string | null = null
+ for (const [regex, name] of DEVICE_PATTERNS) {
+ if (regex.test(ua)) {
+ device = name
+ break
+ }
+ }
+
+ return { browser, operatingSystem, device }
+}
+
+export type LoginMethod = 'password' | 'oauth' | 'webauthn' | 'totp_2fa' | 'email_2fa'
diff --git a/server/utils/user-avatar.ts b/server/utils/user-avatar.ts
new file mode 100644
index 0000000..de76771
--- /dev/null
+++ b/server/utils/user-avatar.ts
@@ -0,0 +1,45 @@
+interface AvatarUserState {
+ avatarProvider?: string | null
+ avatarProviderUserId?: string | null
+}
+
+interface IdentityRow {
+ provider: string
+ providerUserId: string
+ avatar?: string | null
+ createdAt: Date | string
+}
+
+export interface AvatarSource {
+ provider: string | null
+ providerUserId: string | null
+}
+
+export function resolveAvatarSource(
+ user: AvatarUserState,
+ identities: IdentityRow[]
+): AvatarSource | null {
+ if (user.avatarProvider) {
+ return {
+ provider: user.avatarProvider,
+ providerUserId: user.avatarProviderUserId ?? null
+ }
+ }
+
+ const sorted = [...identities].sort((a, b) => {
+ const at = new Date(a.createdAt).getTime()
+ const bt = new Date(b.createdAt).getTime()
+ return at - bt
+ })
+
+ for (const identity of sorted) {
+ if (identity.avatar) {
+ return {
+ provider: identity.provider,
+ providerUserId: identity.providerUserId
+ }
+ }
+ }
+
+ return null
+}