diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 802c003f..d17590ef 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -33,3 +33,8 @@ updates: minor-updates: update-types: - "minor" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index bc581582..c21b8985 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -12,13 +12,13 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Log in to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} @@ -28,7 +28,7 @@ jobs: run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: 1.23.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..53162f5e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: Run Tests + +on: + pull_request: + branches: + - main + - dev + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Copy config file + run: cp config/config.example.yml config/config.yml + + - name: Install dependencies + run: npm ci + + - name: Create database index.ts + run: echo 'export * from "./sqlite";' > server/db/index.ts + + - name: Generate database migrations + run: npm run db:sqlite:generate + + - name: Apply database migrations + run: npm run db:sqlite:push + + - name: Start app in background + run: nohup npm run dev & + + - name: Wait for app availability + run: | + for i in {1..5}; do + if curl --silent --fail http://localhost:3002/auth/login; then + echo "App is up" + exit 0 + fi + echo "Waiting for the app... attempt $i" + sleep 5 + done + echo "App failed to start" + exit 1 + + - name: Build Docker image sqlite + run: make build + + - name: Build Docker image pg + run: make build-pg diff --git a/Dockerfile b/Dockerfile index c2ce2eb3..e1c6e9b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ FROM node:20-alpine AS builder WORKDIR /app # COPY package.json package-lock.json ./ -COPY package.json ./ -RUN npm install +COPY package*.json ./ +RUN npm ci COPY . . @@ -23,8 +23,8 @@ WORKDIR /app RUN apk add --no-cache curl # COPY package.json package-lock.json ./ -COPY package.json ./ -RUN npm install --only=production && npm cache clean --force +COPY package*.json ./ +RUN npm ci --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static diff --git a/Dockerfile.pg b/Dockerfile.pg index 10d440d4..f59a3153 100644 --- a/Dockerfile.pg +++ b/Dockerfile.pg @@ -3,8 +3,8 @@ FROM node:20-alpine AS builder WORKDIR /app # COPY package.json package-lock.json ./ -COPY package.json ./ -RUN npm install +COPY package*.json ./ +RUN npm ci COPY . . @@ -23,8 +23,8 @@ WORKDIR /app RUN apk add --no-cache curl # COPY package.json package-lock.json ./ -COPY package.json ./ -RUN npm install --only=production && npm cache clean --force +COPY package*.json ./ +RUN npm ci --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static diff --git a/Makefile b/Makefile index fdf5daa1..f074c380 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ +.PHONY: build build-pg build-release build-arm build-x86 test clean + build-release: @if [ -z "$(tag)" ]; then \ - echo "Error: tag is required. Usage: make build-all tag="; \ + echo "Error: tag is required. Usage: make build-release tag="; \ exit 1; \ fi docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest -f Dockerfile --push . @@ -15,7 +17,10 @@ build-x86: docker buildx build --platform linux/amd64 -t fosrl/pangolin:latest . build: - docker build -t fosrl/pangolin:latest . + docker build -t fosrl/pangolin:latest -f Dockerfile . + +build-pg: + docker build -t fosrl/pangolin:postgresql-latest -f Dockerfile.pg . test: docker run -it -p 3000:3000 -p 3001:3001 -p 3002:3002 -v ./config:/app/config fosrl/pangolin:latest diff --git a/messages/de-DE.json b/messages/de-DE.json index 09276f72..377ec94b 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Initial Einrichtung des Servers", "initialSetupDescription": "Erstellen Sie das initiale Server-Admin-Konto. Es kann nur einen Server-Admin geben. Sie können diese Anmeldedaten später immer ändern.", "createAdminAccount": "Admin-Konto erstellen", - "setupErrorCreateAdmin": "Beim Erstellen des Server-Admin-Kontos ist ein Fehler aufgetreten." + "setupErrorCreateAdmin": "Beim Erstellen des Server-Admin-Kontos ist ein Fehler aufgetreten.", + "securityKeyManage": "Sicherheitsschlüssel verwalten", + "securityKeyDescription": "Sicherheitsschlüssel für passwortlose Authentifizierung hinzufügen oder entfernen", + "securityKeyRegister": "Neuen Sicherheitsschlüssel registrieren", + "securityKeyList": "Ihre Sicherheitsschlüssel", + "securityKeyNone": "Noch keine Sicherheitsschlüssel registriert", + "securityKeyNameRequired": "Name ist erforderlich", + "securityKeyRemove": "Entfernen", + "securityKeyLastUsed": "Zuletzt verwendet: {date}", + "securityKeyNameLabel": "Name", + "securityKeyNamePlaceholder": "Geben Sie einen Namen für diesen Sicherheitsschlüssel ein", + "securityKeyRegisterSuccess": "Sicherheitsschlüssel erfolgreich registriert", + "securityKeyRegisterError": "Fehler beim Registrieren des Sicherheitsschlüssels", + "securityKeyRemoveSuccess": "Sicherheitsschlüssel erfolgreich entfernt", + "securityKeyRemoveError": "Fehler beim Entfernen des Sicherheitsschlüssels", + "securityKeyLoadError": "Fehler beim Laden der Sicherheitsschlüssel", + "securityKeyLogin": "Mit Sicherheitsschlüssel anmelden", + "securityKeyAuthError": "Fehler bei der Authentifizierung mit Sicherheitsschlüssel", + "securityKeyRecommendation": "Erwägen Sie die Registrierung eines weiteren Sicherheitsschlüssels auf einem anderen Gerät, um sicherzustellen, dass Sie sich nicht aus Ihrem Konto aussperren." } diff --git a/messages/en-US.json b/messages/en-US.json index 01c242b6..48103b04 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -13,7 +13,7 @@ "welcome": "Welcome!", "welcomeTo": "Welcome to", "componentsCreateOrg": "Create an Organization", - "componentsMember": "You're a member of {count, plural, =0 {no organization} =1 {one organization} other {# organizations}}.", + "componentsMember": "You're a member of {count, plural, =0 {no organization} one {one organization} other {# organizations}}.", "componentsInvalidKey": "Invalid or expired license keys detected. Follow license terms to continue using all features.", "dismiss": "Dismiss", "componentsLicenseViolation": "License Violation: This server is using {usedSites} sites which exceeds its licensed limit of {maxSites} sites. Follow license terms to continue using all features.", @@ -251,7 +251,7 @@ "weeks": "Weeks", "months": "Months", "years": "Years", - "day": "{count, plural, =1 {# day} other {# days}}", + "day": "{count, plural, one {# day} other {# days}}", "apiKeysTitle": "API Key Information", "apiKeysConfirmCopy2": "You must confirm that you have copied the API key.", "apiKeysErrorCreate": "Error creating API key", @@ -349,7 +349,7 @@ "licensePurchase": "Purchase License", "licensePurchaseSites": "Purchase Additional Sites", "licenseSitesUsedMax": "{usedSites} of {maxSites} sites used", - "licenseSitesUsed": "{count, plural, =0 {# sites} =1 {# site} other {# sites}} in system.", + "licenseSitesUsed": "{count, plural, =0 {# sites} one {# site} other {# sites}} in system.", "licensePurchaseDescription": "Choose how many sites you want to {selectedMode, select, license {purchase a license for. You can always add more sites later.} other {add to your existing license.}}", "licenseFee": "License fee", "licensePriceSite": "Price per site", @@ -438,7 +438,7 @@ "accessRoleSelect": "Select role", "inviteEmailSentDescription": "An email has been sent to the user with the access link below. They must access the link to accept the invitation.", "inviteSentDescription": "The user has been invited. They must access the link below to accept the invitation.", - "inviteExpiresIn": "The invite will expire in {days, plural, =1 {# day} other {# days}}.", + "inviteExpiresIn": "The invite will expire in {days, plural, one {# day} other {# days}}.", "idpTitle": "Identity Provider", "idpSelect": "Select the identity provider for the external user", "idpNotConfigured": "No identity providers are configured. Please configure an identity provider before creating external users.", @@ -960,6 +960,8 @@ "licenseTierProfessionalRequiredDescription": "This feature is only available in the Professional Edition.", "actionGetOrg": "Get Organization", "actionUpdateOrg": "Update Organization", + "actionUpdateUser": "Update User", + "actionGetUser": "Get User", "actionGetOrgUser": "Get Organization User", "actionListOrgDomains": "List Organization Domains", "actionCreateSite": "Create Site", @@ -1106,7 +1108,7 @@ "containerNetworks": "Networks", "containerHostnameIp": "Hostname/IP", "containerLabels": "Labels", - "containerLabelsCount": "{count} label{s,plural,one{} other{s}}", + "containerLabelsCount": "{count, plural, one {# label} other {# labels}}", "containerLabelsTitle": "Container Labels", "containerLabelEmpty": "", "containerPorts": "Ports", @@ -1118,7 +1120,7 @@ "showStoppedContainers": "Show stopped containers", "noContainersFound": "No containers found. Make sure Docker containers are running.", "searchContainersPlaceholder": "Search across {count} containers...", - "searchResultsCount": "{count} result{s,plural,one{} other{s}}", + "searchResultsCount": "{count, plural, one {# result} other {# results}}", "filters": "Filters", "filterOptions": "Filter Options", "filterPorts": "Ports", @@ -1214,5 +1216,44 @@ "failed": "Failed", "createNewOrgDescription": "Create a new organization", "organization": "Organization", - "port": "Port" + "port": "Port", + "securityKeyManage": "Manage Security Keys", + "securityKeyDescription": "Add or remove security keys for passwordless authentication", + "securityKeyRegister": "Register New Security Key", + "securityKeyList": "Your Security Keys", + "securityKeyNone": "No security keys registered yet", + "securityKeyNameRequired": "Name is required", + "securityKeyRemove": "Remove", + "securityKeyLastUsed": "Last used: {date}", + "securityKeyNameLabel": "Security Key Name", + "securityKeyRegisterSuccess": "Security key registered successfully", + "securityKeyRegisterError": "Failed to register security key", + "securityKeyRemoveSuccess": "Security key removed successfully", + "securityKeyRemoveError": "Failed to remove security key", + "securityKeyLoadError": "Failed to load security keys", + "securityKeyLogin": "Sign in with security key", + "securityKeyAuthError": "Failed to authenticate with security key", + "securityKeyRecommendation": "Register a backup security key on another device to ensure you always have access to your account.", + "registering": "Registering...", + "securityKeyPrompt": "Please verify your identity using your security key. Make sure your security key is connected and ready.", + "securityKeyBrowserNotSupported": "Your browser doesn't support security keys. Please use a modern browser like Chrome, Firefox, or Safari.", + "securityKeyPermissionDenied": "Please allow access to your security key to continue signing in.", + "securityKeyRemovedTooQuickly": "Please keep your security key connected until the sign-in process completes.", + "securityKeyNotSupported": "Your security key may not be compatible. Please try a different security key.", + "securityKeyUnknownError": "There was a problem using your security key. Please try again.", + "twoFactorRequired": "Two-factor authentication is required to register a security key.", + "twoFactor": "Two-Factor Authentication", + "adminEnabled2FaOnYourAccount": "Your administrator has enabled two-factor authentication for {email}. Please complete the setup process to continue.", + "continueToApplication": "Continue to Application", + "securityKeyAdd": "Add Security Key", + "securityKeyRegisterTitle": "Register New Security Key", + "securityKeyRegisterDescription": "Connect your security key and enter a name to identify it", + "securityKeyTwoFactorRequired": "Two-Factor Authentication Required", + "securityKeyTwoFactorDescription": "Please enter your two-factor authentication code to register the security key", + "securityKeyTwoFactorRemoveDescription": "Please enter your two-factor authentication code to remove the security key", + "securityKeyTwoFactorCode": "Two-Factor Code", + "securityKeyRemoveTitle": "Remove Security Key", + "securityKeyRemoveDescription": "Enter your password to remove the security key \"{name}\"", + "securityKeyNoKeysRegistered": "No security keys registered", + "securityKeyNoKeysDescription": "Add a security key to enhance your account security" } diff --git a/messages/es-ES.json b/messages/es-ES.json index 60856fe8..226c02b6 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Configuración inicial del servidor", "initialSetupDescription": "Cree la cuenta de administrador del servidor inicial. Solo puede existir un administrador del servidor. Siempre puede cambiar estas credenciales más tarde.", "createAdminAccount": "Crear cuenta de administrador", - "setupErrorCreateAdmin": "Se produjo un error al crear la cuenta de administrador del servidor." + "setupErrorCreateAdmin": "Se produjo un error al crear la cuenta de administrador del servidor.", + "securityKeyManage": "Gestionar llaves de seguridad", + "securityKeyDescription": "Agregar o eliminar llaves de seguridad para autenticación sin contraseña", + "securityKeyRegister": "Registrar nueva llave de seguridad", + "securityKeyList": "Tus llaves de seguridad", + "securityKeyNone": "No hay llaves de seguridad registradas", + "securityKeyNameRequired": "El nombre es requerido", + "securityKeyRemove": "Eliminar", + "securityKeyLastUsed": "Último uso: {date}", + "securityKeyNameLabel": "Nombre", + "securityKeyNamePlaceholder": "Ingrese un nombre para esta llave de seguridad", + "securityKeyRegisterSuccess": "Llave de seguridad registrada exitosamente", + "securityKeyRegisterError": "Error al registrar la llave de seguridad", + "securityKeyRemoveSuccess": "Llave de seguridad eliminada exitosamente", + "securityKeyRemoveError": "Error al eliminar la llave de seguridad", + "securityKeyLoadError": "Error al cargar las llaves de seguridad", + "securityKeyLogin": "Iniciar sesión con llave de seguridad", + "securityKeyAuthError": "Error al autenticar con llave de seguridad", + "securityKeyRecommendation": "Considere registrar otra llave de seguridad en un dispositivo diferente para asegurarse de no quedar bloqueado de su cuenta." } diff --git a/messages/fr-FR.json b/messages/fr-FR.json index a7a237bf..4681f0cc 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -184,7 +184,7 @@ "cancel": "Abandonner", "resourceConfig": "Snippets de configuration", "resourceConfigDescription": "Copiez et collez ces modules de configuration pour configurer votre ressource TCP/UDP", - "resourceAddEntrypoints": "Traefik: Ajouter des points d’entrée", + "resourceAddEntrypoints": "Traefik: Ajouter des points d'entrée", "resourceExposePorts": "Gerbil: Exposer des ports dans Docker Compose", "resourceLearnRaw": "Apprenez à configurer les ressources TCP/UDP", "resourceBack": "Retour aux ressources", @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Configuration initiale du serveur", "initialSetupDescription": "Créer le compte administrateur du serveur initial. Un seul administrateur serveur peut exister. Vous pouvez toujours changer ces informations d'identification plus tard.", "createAdminAccount": "Créer un compte administrateur", - "setupErrorCreateAdmin": "Une erreur s'est produite lors de la création du compte administrateur du serveur." + "setupErrorCreateAdmin": "Une erreur s'est produite lors de la création du compte administrateur du serveur.", + "securityKeyManage": "Gérer les clés de sécurité", + "securityKeyDescription": "Ajouter ou supprimer des clés de sécurité pour l'authentification sans mot de passe", + "securityKeyRegister": "Enregistrer une nouvelle clé de sécurité", + "securityKeyList": "Vos clés de sécurité", + "securityKeyNone": "Aucune clé de sécurité enregistrée", + "securityKeyNameRequired": "Le nom est requis", + "securityKeyRemove": "Supprimer", + "securityKeyLastUsed": "Dernière utilisation : {date}", + "securityKeyNameLabel": "Nom", + "securityKeyNamePlaceholder": "Entrez un nom pour cette clé de sécurité", + "securityKeyRegisterSuccess": "Clé de sécurité enregistrée avec succès", + "securityKeyRegisterError": "Échec de l'enregistrement de la clé de sécurité", + "securityKeyRemoveSuccess": "Clé de sécurité supprimée avec succès", + "securityKeyRemoveError": "Échec de la suppression de la clé de sécurité", + "securityKeyLoadError": "Échec du chargement des clés de sécurité", + "securityKeyLogin": "Se connecter avec une clé de sécurité", + "securityKeyAuthError": "Échec de l'authentification avec la clé de sécurité", + "securityKeyRecommendation": "Envisagez d'enregistrer une autre clé de sécurité sur un appareil différent pour vous assurer de ne pas être bloqué de votre compte." } diff --git a/messages/it-IT.json b/messages/it-IT.json index cfe983d2..0af5e8e4 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Impostazione Iniziale del Server", "initialSetupDescription": "Crea l'account amministratore del server iniziale. Può esistere solo un amministratore del server. È sempre possibile modificare queste credenziali in seguito.", "createAdminAccount": "Crea Account Admin", - "setupErrorCreateAdmin": "Si è verificato un errore durante la creazione dell'account amministratore del server." + "setupErrorCreateAdmin": "Si è verificato un errore durante la creazione dell'account amministratore del server.", + "securityKeyManage": "Gestisci chiavi di sicurezza", + "securityKeyDescription": "Aggiungi o rimuovi chiavi di sicurezza per l'autenticazione senza password", + "securityKeyRegister": "Registra nuova chiave di sicurezza", + "securityKeyList": "Le tue chiavi di sicurezza", + "securityKeyNone": "Nessuna chiave di sicurezza registrata", + "securityKeyNameRequired": "Il nome è obbligatorio", + "securityKeyRemove": "Rimuovi", + "securityKeyLastUsed": "Ultimo utilizzo: {date}", + "securityKeyNameLabel": "Nome", + "securityKeyNamePlaceholder": "Inserisci un nome per questa chiave di sicurezza", + "securityKeyRegisterSuccess": "Chiave di sicurezza registrata con successo", + "securityKeyRegisterError": "Errore durante la registrazione della chiave di sicurezza", + "securityKeyRemoveSuccess": "Chiave di sicurezza rimossa con successo", + "securityKeyRemoveError": "Errore durante la rimozione della chiave di sicurezza", + "securityKeyLoadError": "Errore durante il caricamento delle chiavi di sicurezza", + "securityKeyLogin": "Accedi con chiave di sicurezza", + "securityKeyAuthError": "Errore durante l'autenticazione con chiave di sicurezza", + "securityKeyRecommendation": "Considera di registrare un'altra chiave di sicurezza su un dispositivo diverso per assicurarti di non rimanere bloccato fuori dal tuo account." } diff --git a/messages/ko-KR.json b/messages/ko-KR.json new file mode 100644 index 00000000..62b1dfc1 --- /dev/null +++ b/messages/ko-KR.json @@ -0,0 +1,1136 @@ +{ + "setupCreate": "조직, 사이트 및 리소스를 생성하십시오.", + "orgDisplayName": "이것은 귀하의 조직의 표시 이름입니다.", + "setupIdentifierMessage": "이것은 귀하의 조직에 대한 고유 식별자입니다. 표시 이름과는 별개입니다.", + "componentsErrorNoMemberCreate": "현재 어떤 조직의 구성원도 아닙니다. 시작하려면 조직을 생성하세요.", + "componentsInvalidKey": "유효하지 않거나 만료된 라이센스 키가 감지되었습니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.", + "orgId": "조직 ID", + "siteQuestionRemove": "조직에서 사이트 {selectedSite}를 제거하시겠습니까?", + "siteCreateDescription2": "아래 단계를 따라 새 사이트를 생성하고 연결하십시오", + "componentsLicenseViolation": "라이센스 위반: 이 서버는 {usedSites} 사이트를 사용하고 있으며, 이는 {maxSites} 사이트의 라이센스 한도를 초과합니다. 모든 기능을 계속 사용하려면 라이센스 조건을 따르십시오.", + "years": "연도", + "hours": "시간", + "days": "일", + "weeks": "주", + "months": "개월", + "authCreateAccount": "시작하려면 계정을 생성하세요.", + "email": "이메일", + "password": "비밀번호", + "confirmPassword": "비밀번호 확인", + "createAccount": "계정 생성", + "viewSettings": "설정 보기", + "delete": "삭제", + "name": "이름", + "online": "온라인", + "offline": "오프라인", + "site": "사이트", + "dataIn": "데이터 입력", + "dataOut": "데이터 출력", + "connectionType": "연결 유형", + "tunnelType": "터널 유형", + "local": "로컬", + "edit": "편집", + "siteConfirmDelete": "사이트 삭제 확인", + "siteDelete": "사이트 삭제", + "siteMessageRemove": "제거되면 사이트에 더 이상 접근할 수 없습니다. 사이트와 관련된 모든 리소스와 대상도 제거됩니다.", + "siteMessageConfirm": "확인을 위해 아래에 사이트 이름을 입력해 주세요.", + "setupNewOrg": "새 조직", + "setupCreateOrg": "조직 생성", + "setupCreateResources": "리소스 생성", + "setupOrgName": "조직 이름", + "setupErrorIdentifier": "조직 ID가 이미 사용 중입니다. 다른 것을 선택해 주세요.", + "componentsErrorNoMember": "현재 어떤 조직의 구성원도 아닙니다.", + "welcome": "판골린에 오신 것을 환영합니다.", + "componentsCreateOrg": "조직 생성", + "componentsMember": "당신은 {count, plural, =0 {조직이 없습니다} one {하나의 조직} other {# 개의 조직}}의 구성원입니다.", + "componentsSupporterMessage": "{tier}로 판골린을 지원해 주셔서 감사합니다!", + "inviteErrorNotValid": "죄송하지만, 접근하려는 초대가 수락되지 않았거나 더 이상 유효하지 않은 것 같습니다.", + "inviteErrorUser": "죄송하지만, 접근하려는 초대가 이 사용자에게 해당되지 않는 것 같습니다.", + "inviteLoginUser": "올바른 사용자로 로그인했는지 확인하십시오.", + "inviteErrorNoUser": "죄송하지만, 접근하려는 초대가 존재하지 않는 사용자에 대한 것인 것 같습니다.", + "inviteCreateUser": "먼저 계정을 생성해 주세요.", + "goHome": "홈으로 가기", + "inviteLogInOtherUser": "다른 사용자로 로그인", + "createAnAccount": "계정 만들기", + "siteManageSites": "사이트 관리", + "siteDescription": "안전한 터널을 통해 네트워크에 연결할 수 있도록 허용", + "siteCreate": "사이트 생성", + "inviteNotAccepted": "초대가 수락되지 않음", + "authNoAccount": "계정이 없으신가요?", + "siteCreateDescription": "리소스를 연결하기 위해 새 사이트를 생성하십시오.", + "dismiss": "해제", + "close": "닫기", + "siteErrorCreate": "사이트 생성 오류", + "siteErrorCreateKeyPair": "키 쌍 또는 사이트 기본값을 찾을 수 없습니다", + "siteErrorCreateDefaults": "사이트 기본값을 찾을 수 없습니다", + "siteNameDescription": "이것은 사이트의 표시 이름입니다.", + "method": "방법", + "siteMethodDescription": "이것이 연결을 노출하는 방법입니다.", + "siteLearnNewt": "시스템에 Newt 설치하는 방법 배우기", + "siteSeeConfigOnce": "구성을 한 번만 볼 수 있습니다.", + "siteLoadWGConfig": "WireGuard 구성 로딩 중...", + "siteDocker": "Docker 배포 세부정보 확장", + "toggle": "전환", + "dockerCompose": "도커 컴포즈", + "dockerRun": "도커 실행", + "siteLearnLocal": "로컬 사이트는 터널링하지 않습니다. 자세히 알아보기", + "siteConfirmCopy": "구성을 복사했습니다.", + "searchSitesProgress": "사이트 검색...", + "siteAdd": "사이트 추가", + "siteInstallNewt": "Newt 설치", + "siteInstallNewtDescription": "시스템에서 Newt 실행하기", + "WgConfiguration": "WireGuard 구성", + "WgConfigurationDescription": "네트워크에 연결하기 위한 다음 구성을 사용하십시오.", + "operatingSystem": "운영 체제", + "commands": "명령", + "recommended": "추천", + "siteNewtDescription": "최고의 사용자 경험을 위해 Newt를 사용하십시오. Newt는 WireGuard를 기반으로 하며, 판골린 대시보드 내에서 개인 네트워크의 LAN 주소로 개인 리소스에 접근할 수 있도록 합니다.", + "siteRunsInDocker": "Docker에서 실행", + "siteRunsInShell": "macOS, Linux 및 Windows에서 셸에서 실행", + "siteErrorDelete": "사이트 삭제 오류", + "siteErrorUpdate": "사이트 업데이트에 실패했습니다", + "siteErrorUpdateDescription": "사이트 업데이트 중 오류가 발생했습니다.", + "siteUpdated": "사이트가 업데이트되었습니다", + "siteUpdatedDescription": "사이트가 업데이트되었습니다.", + "siteGeneralDescription": "이 사이트에 대한 일반 설정을 구성하세요.", + "siteSettingDescription": "사이트에서 설정을 구성하세요", + "siteSetting": "{siteName} 설정", + "siteNewtTunnel": "뉴트 터널 (추천)", + "siteNewtTunnelDescription": "네트워크에 대한 진입점을 생성하는 가장 쉬운 방법입니다. 추가 설정이 필요 없습니다.", + "siteWg": "기본 WireGuard", + "siteWgDescription": "모든 WireGuard 클라이언트를 사용하여 터널을 설정하세요. 수동 NAT 설정이 필요합니다.", + "siteLocalDescription": "로컬 리소스만 사용 가능합니다. 터널링이 없습니다.", + "siteSeeAll": "모든 사이트 보기", + "siteTunnelDescription": "사이트에 연결하는 방법을 결정하세요", + "siteNewtCredentials": "Newt 자격 증명", + "siteNewtCredentialsDescription": "이것이 Newt가 서버와 인증하는 방법입니다", + "orgPolicyDeletedDescription": "정책이 성공적으로 삭제되었습니다", + "actionCreateResourceRule": "리소스 규칙 생성", + "defaultMappingsUpdatedDescription": "기본 매핑이 성공적으로 업데이트되었습니다.", + "orgPoliciesAbout": "조직 정책에 대하여", + "orgPoliciesAboutDescription": "조직 정책은 사용자의 ID 토큰에 따라 조직에 대한 액세스를 제어하는 데 사용됩니다. ID 토큰에서 역할 및 조직 정보를 추출하기 위해 JMESPath 표현식을 지정할 수 있습니다.", + "orgPoliciesAboutDescriptionLink": "자세한 내용은 문서를 참조하십시오.", + "actionDeleteResourceRule": "리소스 규칙 삭제", + "defaultMappingsOptional": "기본 매핑(선택 사항)", + "signupError": "가입하는 동안 오류가 발생했습니다.", + "siteCredentialsSave": "자격 증명 저장", + "siteCredentialsSaveDescription": "이것은 한 번만 볼 수 있습니다. 안전한 장소에 복사해 두세요.", + "siteInfo": "사이트 정보", + "status": "상태", + "shareTitle": "공유 링크 관리", + "shareDescription": "공유 가능한 링크를 생성하여 리소스에 대한 임시 또는 영구 액세스를 부여합니다.", + "shareSearch": "공유 링크 검색...", + "shareCreate": "공유 링크 생성", + "shareErrorDelete": "링크 삭제에 실패했습니다.", + "shareErrorDeleteMessage": "링크 삭제 중 오류가 발생했습니다.", + "shareDeleted": "링크가 삭제되었습니다.", + "shareDeletedDescription": "링크가 삭제되었습니다.", + "shareTokenDescription": "액세스 토큰은 쿼리 매개변수 또는 요청 헤더의 두 가지 방법으로 전달될 수 있습니다. 이는 인증된 액세스를 위해 클라이언트에서 모든 요청마다 전달되어야 합니다.", + "accessToken": "액세스 토큰", + "usageExamples": "사용 예", + "tokenId": "토큰 ID", + "requestHeades": "요청 헤더", + "queryParameter": "쿼리 매개변수", + "importantNote": "중요한 참고 사항", + "shareImportantDescription": "보안상의 이유로 가능한 경우 쿼리 매개변수보다 헤더를 사용하는 것이 권장됩니다. 쿼리 매개변수는 서버 로그나 브라우저 기록에 기록될 수 있습니다.", + "token": "토큰", + "shareTokenSecurety": "액세스 토큰을 안전하게 유지하세요. 공개적으로 접근 가능한 영역이나 클라이언트 측 코드에서 공유하지 마세요.", + "shareErrorFetchResource": "리소스를 가져오는 데 실패했습니다.", + "shareErrorFetchResourceDescription": "리소스를 가져오는 중 오류가 발생했습니다.", + "shareErrorCreate": "공유 링크 생성에 실패했습니다.", + "shareErrorCreateDescription": "공유 링크를 생성하는 동안 오류가 발생했습니다", + "shareCreateDescription": "이 링크가 있는 누구나 리소스에 접근할 수 있습니다.", + "shareTitleOptional": "제목 (선택 사항)", + "expireIn": "만료됨", + "neverExpire": "만료되지 않음", + "shareExpireDescription": "만료 시간은 링크가 사용 가능하고 리소스에 접근할 수 있는 기간입니다. 이 시간이 지나면 링크는 더 이상 작동하지 않으며, 이 링크를 사용한 사용자는 리소스에 대한 접근 권한을 잃게 됩니다.", + "pangolinLogoAlt": "판골린 로고", + "shareSeeOnce": "이 링크는 한 번만 볼 수 있습니다. 반드시 복사해 두세요.", + "shareAccessHint": "이 링크가 있는 누구나 리소스에 접근할 수 있습니다. 주의해서 공유하세요.", + "shareTokenUsage": "액세스 토큰 사용 보기", + "createLink": "링크 생성", + "resourcesNotFound": "리소스가 발견되지 않았습니다.", + "resourceSearch": "리소스 검색", + "openMenu": "메뉴 열기", + "resource": "리소스", + "title": "제목", + "created": "생성됨", + "expires": "만료", + "never": "절대", + "shareErrorSelectResource": "리소스를 선택하세요", + "resourceTitle": "리소스 관리", + "resourceDescription": "개인 애플리케이션에 대한 보안 프록시 생성", + "resourcesSearch": "리소스 검색...", + "resourceAdd": "리소스 추가", + "resourceErrorDelte": "리소스 삭제 중 오류 발생", + "authentication": "인증", + "protected": "보호됨", + "notProtected": "보호되지 않음", + "inviteAlready": "초대받은 것 같습니다!", + "resourceMessageRemove": "제거되면 리소스에 더 이상 접근할 수 없습니다. 리소스와 연결된 모든 대상도 제거됩니다.", + "resourceMessageConfirm": "확인을 위해 아래에 리소스의 이름을 입력하세요.", + "tagsEnteredDescription": "입력한 태그는 다음과 같습니다.", + "resourceQuestionRemove": "조직에서 리소스 {selectedResource}를 제거하시겠습니까?", + "resourceHTTP": "HTTPS 리소스", + "resourceHTTPDescription": "서브도메인 또는 기본 도메인을 사용하여 HTTPS를 통해 앱에 대한 요청을 프록시합니다.", + "resourceRaw": "원시 TCP/UDP 리소스", + "resourceRawDescription": "TCP/UDP를 통해 포트 번호를 사용하여 앱에 요청을 프록시합니다.", + "resourceCreate": "리소스 생성", + "resourceCreateDescription": "아래 단계를 따라 새 리소스를 생성하세요.", + "resourceSeeAll": "모든 리소스 보기", + "resourceInfo": "리소스 정보", + "resourceNameDescription": "이것은 리소스의 표시 이름입니다.", + "siteSelect": "사이트 선택", + "siteSearch": "사이트 검색", + "siteNotFound": "사이트를 찾을 수 없습니다.", + "otpEnable": "이중 인증 활성화", + "siteSelectionDescription": "이 사이트는 리소스에 대한 연결을 제공합니다.", + "resourceType": "리소스 유형", + "resourceTypeDescription": "리소스에 접근하는 방법을 결정하세요", + "resourceHTTPSSettings": "HTTPS 설정", + "resourceHTTPSSettingsDescription": "리소스에 대한 HTTPS 접근 방식을 구성하십시오.", + "domainType": "도메인 유형", + "subdomain": "서브도메인", + "baseDomain": "기본 도메인", + "subdomnainDescription": "리소스에 접근할 수 있는 하위 도메인입니다.", + "resourceRawSettings": "TCP/UDP 설정", + "otpDisable": "이중 인증 비활성화", + "resourceRawSettingsDescription": "TCP/UDP를 통해 리소스에 접근하는 방법을 구성하세요.", + "protocol": "프로토콜", + "protocolSelect": "프로토콜 선택", + "resourcePortNumber": "포트 번호", + "logout": "로그 아웃", + "resourcePortNumberDescription": "요청을 프록시하기 위한 외부 포트 번호입니다.", + "cancel": "취소", + "resourceConfig": "구성 스니펫", + "inviteAlreadyDescription": "초대를 수락하려면 로그인하거나 계정을 생성해야 합니다.", + "resourceConfigDescription": "TCP/UDP 리소스를 설정하기 위해 이 구성 스니펫을 복사하여 붙여넣으십시오.", + "resourceAddEntrypoints": "Traefik: 엔트리포인트 추가", + "resourceExposePorts": "Gerbil: Docker Compose에서 포트 노출", + "resourceLearnRaw": "TCP/UDP 리소스 구성 방법 알아보기", + "resourceBack": "리소스로 돌아가기", + "resourceGoTo": "리소스로 이동", + "resourceDelete": "리소스 삭제", + "resourceDeleteConfirm": "리소스 삭제 확인", + "visibility": "가시성", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "general": "일반", + "generalSettings": "일반 설정", + "proxy": "프록시", + "rules": "규칙", + "resourceSettingDescription": "리소스의 설정을 구성하세요.", + "sidebarApiKeys": "API 키", + "resourceSetting": "{resourceName} 설정", + "alwaysAllow": "항상 허용", + "alwaysDeny": "항상 거부", + "orgSettingsDescription": "조직의 일반 설정을 구성하세요", + "orgGeneralSettings": "조직 설정", + "orgGeneralSettingsDescription": "조직 세부정보 및 구성을 관리하세요.", + "saveGeneralSettings": "일반 설정 저장", + "orgDangerZone": "위험 구역", + "orgDangerZoneDescription": "이 조직을 삭제하면 되돌릴 수 없습니다. 확실히 하세요.", + "orgDelete": "조직 삭제", + "orgDeleteConfirm": "조직 삭제 확인", + "orgMessageRemove": "이 작업은 되돌릴 수 없으며 모든 관련 데이터를 삭제합니다.", + "orgMessageConfirm": "확인을 위해 아래에 조직 이름을 입력하십시오.", + "orgQuestionRemove": "조직 {selectedOrg}을(를) 제거하시겠습니까?", + "orgUpdated": "조직이 업데이트되었습니다.", + "orgUpdatedDescription": "조직이 업데이트되었습니다.", + "orgErrorUpdate": "조직 업데이트에 실패했습니다.", + "orgErrorUpdateMessage": "조직을 업데이트하는 동안 오류가 발생했습니다.", + "sidebarSettings": "설정", + "orgErrorFetch": "조직을 가져오는 데 실패했습니다.", + "orgErrorFetchMessage": "조직을 나열하는 동안 오류가 발생했습니다", + "orgErrorDelete": "조직 삭제에 실패했습니다.", + "orgErrorDeleteMessage": "조직을 삭제하는 중 오류가 발생했습니다.", + "orgDeleted": "조직이 삭제되었습니다.", + "orgDeletedMessage": "조직과 그 데이터가 삭제되었습니다.", + "orgMissing": "조직 ID가 누락되었습니다", + "orgMissingMessage": "조직 ID 없이 초대장을 재생성할 수 없습니다.", + "accessUsersManage": "사용자 관리", + "accessUsersDescription": "사용자를 초대하고 역할에 추가하여 조직에 대한 접근을 관리하세요", + "accessUsersSearch": "사용자 검색...", + "accessUserCreate": "사용자 생성", + "accessUserRemove": "사용자 제거", + "username": "사용자 이름", + "identityProvider": "아이덴티티 공급자", + "role": "역할", + "nameRequired": "이름은 필수입니다", + "accessRolesManage": "역할 관리", + "accessRolesDescription": "조직에 대한 액세스를 관리할 역할 구성", + "accessRolesSearch": "역할 검색...", + "accessRolesAdd": "역할 추가", + "accessRoleDelete": "역할 삭제", + "description": "설명", + "inviteTitle": "열린 초대", + "inviteDescription": "다른 사용자에 대한 초대를 관리하세요", + "inviteSearch": "초대 검색...", + "minutes": "분", + "day": "{count, plural, one {#일} other {#일}}", + "apiKeysTitle": "API 키 정보", + "signupQuestion": "이미 계정이 있습니까?", + "apiKeysConfirmCopy2": "API 키를 복사했음을 확인해야 합니다.", + "apiKeysErrorCreate": "API 키 생성 오류", + "apiKeysErrorSetPermission": "권한 설정 오류", + "apiKeysCreate": "API 키 생성", + "apiKeysCreateDescription": "조직을 위한 새로운 API 키 생성", + "apiKeysGeneralSettings": "권한", + "apiKeysGeneralSettingsDescription": "이 API 키가 수행할 수 있는 작업 결정", + "apiKeysList": "귀하의 API 키", + "apiKeysSave": "API 키 저장", + "apiKeysSaveDescription": "이것은 한 번만 볼 수 있습니다. 안전한 장소에 복사해 두세요.", + "apiKeysInfo": "귀하의 API 키는 다음과 같습니다:", + "apiKeysConfirmCopy": "API 키를 복사했습니다", + "generate": "생성", + "done": "완료", + "apiKeysSeeAll": "모든 API 키 보기", + "apiKeysPermissionsErrorLoadingActions": "API 키 작업 로드 오류", + "apiKeysPermissionsErrorUpdate": "권한 설정 오류", + "apiKeysPermissionsUpdated": "권한이 업데이트되었습니다", + "login": "로그인", + "apiKeysPermissionsUpdatedDescription": "권한이 업데이트되었습니다.", + "apiKeysPermissionsGeneralSettings": "권한", + "apiKeysPermissionsGeneralSettingsDescription": "이 API 키가 수행할 수 있는 작업 결정", + "apiKeysPermissionsSave": "권한 저장", + "apiKeysPermissionsTitle": "권한", + "apiKeys": "API 키", + "searchApiKeys": "API 키 검색...", + "apiKeysAdd": "API 키 생성", + "apiKeysErrorDelete": "API 키 삭제 오류", + "apiKeysErrorDeleteMessage": "API 키 삭제 오류", + "apiKeysQuestionRemove": "조직에서 API 키 {selectedApiKey}를 제거하시겠습니까?", + "apiKeysMessageRemove": "삭제되면 API 키를 더 이상 사용할 수 없습니다.", + "apiKeysMessageConfirm": "확인을 위해 아래에 API 키의 이름을 입력해 주세요.", + "apiKeysDeleteConfirm": "API 키 삭제 확인", + "apiKeysDelete": "API 키 삭제", + "apiKeysManage": "API 키 관리", + "apiKeysDescription": "API 키는 통합 API와 인증하는 데 사용됩니다.", + "apiKeysSettings": "{apiKeyName} 설정", + "userTitle": "모든 사용자 관리", + "userDescription": "시스템의 모든 사용자를 보고 관리합니다", + "userAbount": "사용자 관리에 대한 정보", + "userAbountDescription": "이 표는 시스템의 모든 루트 사용자 객체를 표시합니다. 각 사용자는 여러 조직에 속할 수 있습니다. 사용자를 조직에서 제거해도 루트 사용자 객체는 삭제되지 않으며, 시스템에 남아 있습니다. 사용자를 시스템에서 완전히 제거하려면 이 표의 삭제 작업을 사용하여 루트 사용자 객체를 삭제해야 합니다.", + "userServer": "서버 사용자", + "userSearch": "서버 사용자 검색 중...", + "userErrorDelete": "사용자 삭제 오류", + "userDeleteConfirm": "사용자 삭제 확인", + "userDeleteServer": "서버에서 사용자 삭제", + "userMessageRemove": "사용자가 모든 조직에서 제거되며 서버에서 완전히 삭제됩니다.", + "userMessageConfirm": "확인을 위해 아래에 사용자 이름을 입력하십시오.", + "userQuestionRemove": "정말로 {selectedUser}를 서버에서 영구적으로 삭제하시겠습니까?", + "licenseKey": "라이센스 키", + "valid": "유효", + "numberOfSites": "사이트 수", + "licenseKeySearch": "라이센스 키 검색 중...", + "licenseKeyAdd": "라이센스 키 추가", + "type": "유형", + "licenseKeyRequired": "라이센스 키가 필요합니다", + "licenseTermsAgree": "라이선스 조건에 동의해야 합니다.", + "licenseErrorKeyLoad": "라이센스 키를 로드하는 데 실패했습니다.", + "licenseErrorKeyLoadDescription": "라이센스 키 로드 중 오류가 발생했습니다.", + "licenseErrorKeyDelete": "라이센스 키 삭제에 실패했습니다.", + "resourceNotFound": "리소스를 찾을 수 없습니다", + "licenseErrorKeyDeleteDescription": "라이센스 키 삭제 중 오류가 발생했습니다.", + "licenseKeyDeleted": "라이센스 키가 삭제되었습니다.", + "licenseKeyDeletedDescription": "라이센스 키가 삭제되었습니다.", + "licenseErrorKeyActivate": "라이센스 키 활성화에 실패했습니다.", + "licenseErrorKeyActivateDescription": "라이센스 키를 활성화하는 동안 오류가 발생했습니다", + "licenseAbout": "라이센스에 대한 정보", + "communityEdition": "커뮤니티 에디션", + "licenseAboutDescription": "이것은 상업적 환경에서 Pangolin을 사용하는 비즈니스 및 기업 사용자용입니다. 개인 용도로 Pangolin을 사용하는 경우 이 섹션을 무시할 수 있습니다.", + "licenseKeyActivated": "라이센스 키가 활성화되었습니다", + "licenseKeyActivatedDescription": "라이센스 키가 성공적으로 활성화되었습니다.", + "licenseErrorKeyRecheck": "라이센스 키 재확인 실패", + "licenseErrorKeyRecheckDescription": "라이센스 키를 재확인하는 중 오류가 발생했습니다.", + "licenseErrorKeyRechecked": "라이센스 키가 재확인되었습니다.", + "licenseErrorKeyRecheckedDescription": "모든 라이센스 키가 재검사되었습니다.", + "licenseActivateKey": "라이센스 키 활성화", + "licenseActivateKeyDescription": "라이센스 키를 입력하여 활성화하십시오.", + "licenseActivate": "라이센스 활성화", + "licenseAgreement": "이 상자를 체크함으로써, 귀하는 귀하의 라이선스 키와 관련된 계층에 해당하는 라이선스 조건을 읽고 동의했음을 확인합니다.", + "fossorialLicense": "Fossorial 상업 라이선스 및 구독 약관 보기", + "licenseMessageRemove": "이 작업은 라이센스 키와 그에 의해 부여된 모든 관련 권한을 제거합니다.", + "sidebarAllUsers": "모든 사용자", + "licenseMessageConfirm": "확인을 위해 아래에 라이센스 키를 입력하세요.", + "licenseQuestionRemove": "라이센스 키 {selectedKey}를 삭제하시겠습니까?", + "licenseKeyDelete": "라이센스 키 삭제", + "licenseKeyDeleteConfirm": "라이센스 키 삭제 확인", + "licenseTitle": "라이선스 상태 관리", + "licenseTitleDescription": "시스템에서 라이센스 키를 보고 관리합니다.", + "licenseHost": "호스트 라이센스", + "licenseHostDescription": "호스트의 주요 라이센스 키를 관리합니다.", + "licensedNot": "라이센스 없음", + "hostId": "호스트 ID", + "licenseReckeckAll": "모든 키 재확인", + "licenseSiteUsage": "사이트 사용량", + "licenseSiteUsageDecsription": "이 라이센스를 사용하는 사이트 수를 확인하세요.", + "noResults": "결과를 찾을 수 없습니다.", + "licenseNoSiteLimit": "라이선스가 없는 호스트를 사용하는 사이트 수에 제한이 없습니다.", + "licensePurchase": "라이센스 구매", + "licensePurchaseSites": "추가 사이트 구매", + "licenseSitesUsedMax": "{maxSites}개의 사이트 중 {usedSites}개 사용 중", + "licenseSitesUsed": "시스템에 {count, plural, =0 {# 사이트} one {# 사이트} other {# 사이트}}가 있습니다.", + "licensePurchaseDescription": "구매할 사이트 수를 선택하세요 {selectedMode, select, license {라이센스를 구매합니다. 나중에 더 많은 사이트를 추가할 수 있습니다.} other {기존 라이센스에 추가합니다.}}", + "licenseFee": "라이선스 요금", + "licensePriceSite": "사이트당 가격", + "total": "총계", + "licenseContinuePayment": "결제로 진행", + "pricingPage": "가격 페이지", + "pricingPortal": "구매 포털 보기", + "licensePricingPage": "가장 최신의 가격 및 할인 정보를 보려면 방문하십시오 ", + "invite": "초대", + "inviteRegenerate": "초대장 재생성", + "inviteRegenerateDescription": "이전 초대를 취소하고 새로 생성", + "inviteRemove": "초대 제거", + "inviteRemoveError": "초대 제거 실패", + "inviteRemoveErrorDescription": "초대를 제거하는 동안 오류가 발생했습니다.", + "inviteRemoved": "초대가 제거되었습니다.", + "inviteRemovedDescription": "{email}에 대한 초대가 삭제되었습니다.", + "inviteQuestionRemove": "초대 {email}를 제거하시겠습니까?", + "inviteMessageRemove": "한 번 제거되면 이 초대는 더 이상 유효하지 않습니다. 나중에 사용자를 다시 초대할 수 있습니다.", + "inviteMessageConfirm": "확인을 위해 아래 초대의 이메일 주소를 입력해 주세요.", + "inviteQuestionRegenerate": "{email}에 대한 초대장을 다시 생성하시겠습니까? 이전 초대장은 취소됩니다.", + "inviteRemoveConfirm": "초대 제거 확인", + "inviteRegenerated": "초대 재생성됨", + "inviteSent": "새 초대장이 {email}로 전송되었습니다.", + "inviteSentEmail": "사용자에게 이메일 알림 전송", + "inviteGenerate": "{email}에 대한 새로운 초대장이 생성되었습니다.", + "inviteDuplicateError": "초대 중복", + "inviteDuplicateErrorDescription": "이 사용자에 대한 초대가 이미 존재합니다.", + "inviteRateLimitError": "요청 한도 초과", + "inviteRateLimitErrorDescription": "시간당 3회 재생성 한도를 초과했습니다. 나중에 다시 시도하세요.", + "inviteRegenerateError": "초대 재생성 실패", + "inviteRegenerateErrorDescription": "초대장을 재생성하는 동안 오류가 발생했습니다.", + "inviteValidityPeriod": "유효 기간", + "inviteValidityPeriodSelect": "유효 기간 선택", + "inviteRegenerateMessage": "초대장이 다시 생성되었습니다. 사용자는 아래 링크에 접속하여 초대장을 수락해야 합니다.", + "inviteRegenerateButton": "재생성", + "expiresAt": "만료 시간", + "accessRoleUnknown": "알 수 없는 역할", + "placeholder": "자리 표시자", + "userErrorOrgRemove": "사용자를 제거하지 못했습니다", + "userErrorOrgRemoveDescription": "사용자를 제거하는 동안 오류가 발생했습니다.", + "userOrgRemoved": "사용자가 제거되었습니다.", + "userOrgRemovedDescription": "사용자 {email}가 조직에서 제거되었습니다.", + "userQuestionOrgRemove": "{email}을 조직에서 제거하시겠습니까?", + "userMessageOrgRemove": "이 사용자가 제거되면 더 이상 조직에 접근할 수 없습니다. 나중에 다시 초대할 수 있지만, 초대를 다시 수락해야 합니다.", + "userMessageOrgConfirm": "확인을 위해 아래에 사용자 이름을 입력하세요.", + "userRemoveOrgConfirm": "사용자 제거 확인", + "userRemoveOrg": "조직에서 사용자 제거", + "users": "사용자", + "accessRoleMember": "회원", + "accessRoleOwner": "소유자", + "userConfirmed": "확인됨", + "idpNameInternal": "내부", + "emailInvalid": "유효하지 않은 이메일 주소입니다.", + "inviteValidityDuration": "지속 시간을 선택하십시오.", + "accessRoleSelectPlease": "역할을 선택하세요", + "usernameRequired": "사용자 이름은 필수입니다.", + "idpSelectPlease": "신원 제공자를 선택하십시오", + "idpGenericOidc": "일반 OAuth2/OIDC 공급자.", + "accessRoleErrorFetch": "역할을 가져오는 데 실패했습니다.", + "accessRoleErrorFetchDescription": "역할을 가져오는 중 오류가 발생했습니다.", + "idpErrorFetch": "신원 제공자를 가져오는 데 실패했습니다", + "idpErrorFetchDescription": "신원 공급자를 가져오는 중 오류가 발생했습니다.", + "userErrorExists": "사용자가 이미 존재합니다.", + "terabytes": "{count} TB", + "userErrorExistsDescription": "이 사용자는 이미 조직의 구성원입니다.", + "inviteError": "사용자 초대에 실패했습니다", + "inviteErrorDescription": "사용자를 초대하는 동안 오류가 발생했습니다.", + "userInvited": "사용자가 초대되었습니다.", + "userInvitedDescription": "사용자가 성공적으로 초대되었습니다.", + "userErrorCreate": "사용자 생성에 실패했습니다.", + "userErrorCreateDescription": "사용자를 생성하는 동안 오류가 발생했습니다.", + "userCreated": "사용자가 생성되었습니다.", + "userCreatedDescription": "사용자가 성공적으로 생성되었습니다.", + "userTypeInternal": "내부 사용자", + "userTypeInternalDescription": "사용자를 초대하여 귀하의 조직에 직접 참여하게 하세요.", + "userTypeExternal": "외부 사용자", + "userTypeExternalDescription": "외부 신원 공급자를 사용하여 사용자를 생성하세요.", + "accessUserCreateDescription": "새 사용자를 만들기 위한 아래 단계를 따르세요.", + "userSeeAll": "모든 사용자 보기", + "userTypeTitle": "사용자 유형", + "userTypeDescription": "사용자를 생성하는 방법을 결정하세요.", + "userSettings": "사용자 정보", + "userSettingsDescription": "새 사용자에 대한 세부정보를 입력하십시오.", + "inviteEmailSent": "사용자에게 초대 이메일 보내기", + "inviteValid": "유효 기간", + "selectDuration": "지속 시간 선택", + "accessRoleSelect": "역할 선택", + "inviteEmailSentDescription": "아래의 접근 링크와 함께 사용자에게 이메일이 전송되었습니다. 사용자는 초대를 수락하기 위해 링크에 접근해야 합니다.", + "inviteSentDescription": "사용자가 초대되었습니다. 초대를 수락하려면 아래 링크에 접속해야 합니다.", + "inviteExpiresIn": "초대는 {days, plural, one {#일} other {#일}} 후에 만료됩니다.", + "idpTitle": "아이덴티티 공급자", + "idpSelect": "외부 사용자를 위한 아이덴티티 공급자를 선택하십시오", + "idpNotConfigured": "구성된 아이덴티티 공급자가 없습니다. 외부 사용자를 생성하기 전에 아이덴티티 공급자를 구성하십시오.", + "usernameUniq": "선택한 아이덴티티 공급자에 존재하는 고유한 사용자 이름과 일치해야 합니다.", + "emailOptional": "이메일 (선택 사항)", + "nameOptional": "이름 (선택 사항)", + "accessControls": "접근 제어", + "userDescription2": "이 사용자의 설정 관리", + "accessRoleErrorAdd": "사용자를 역할에 추가하는 데 실패했습니다.", + "accessRoleErrorAddDescription": "사용자를 역할에 추가하는 동안 오류가 발생했습니다.", + "userSaved": "사용자 저장됨", + "userSavedDescription": "사용자가 업데이트되었습니다.", + "accessControlsDescription": "이 사용자가 조직에서 접근하고 수행할 수 있는 작업을 관리하세요", + "accessControlsSubmit": "접근 제어 저장", + "roles": "역할", + "accessUsersRoles": "사용자 및 역할 관리", + "accessUsersRolesDescription": "사용자를 초대하고 역할에 추가하여 조직에 대한 접근을 관리하세요", + "key": "키", + "createdAt": "생성일", + "proxyErrorInvalidHeader": "잘못된 사용자 정의 호스트 헤더 값입니다. 도메인 이름 형식을 사용하거나 사용자 정의 호스트 헤더를 해제하려면 비워 두십시오.", + "proxyErrorTls": "유효하지 않은 TLS 서버 이름입니다. 도메인 이름 형식을 사용하거나 비워 두어 TLS 서버 이름을 제거하십시오.", + "proxyEnableSSL": "SSL 활성화 (https)", + "targetErrorFetch": "대상 가져오는 데 실패했습니다.", + "targetErrorFetchDescription": "대상 가져오는 중 오류가 발생했습니다", + "siteErrorFetch": "리소스를 가져오는 데 실패했습니다", + "siteErrorFetchDescription": "리소스를 가져오는 동안 오류가 발생했습니다", + "targetErrorDuplicate": "중복 대상", + "targetErrorDuplicateDescription": "이 설정을 가진 대상이 이미 존재합니다", + "targetWireGuardErrorInvalidIp": "유효하지 않은 대상 IP", + "targetWireGuardErrorInvalidIpDescription": "대상 IP는 사이트 서브넷 내에 있어야 합니다.", + "targetsUpdated": "대상 업데이트됨", + "targetsUpdatedDescription": "대상 및 설정이 성공적으로 업데이트되었습니다.", + "targetsErrorUpdate": "대상 업데이트 실패", + "targetsErrorUpdateDescription": "대상 업데이트 중 오류가 발생했습니다.", + "targetTlsUpdate": "TLS 설정이 업데이트되었습니다.", + "targetTlsUpdateDescription": "TLS 설정이 성공적으로 업데이트되었습니다.", + "targetErrorTlsUpdate": "TLS 설정 업데이트에 실패했습니다.", + "targetErrorTlsUpdateDescription": "TLS 설정을 업데이트하는 동안 오류가 발생했습니다", + "proxyUpdated": "프록시 설정이 업데이트되었습니다.", + "proxyUpdatedDescription": "프록시 설정이 성공적으로 업데이트되었습니다", + "proxyErrorUpdate": "프록시 설정 업데이트에 실패했습니다.", + "proxyErrorUpdateDescription": "프록시 설정을 업데이트하는 동안 오류가 발생했습니다", + "targetAddr": "IP / 호스트 이름", + "targetPort": "포트", + "targetProtocol": "프로토콜", + "targetTlsSettings": "보안 연결 구성", + "targetTlsSettingsDescription": "리소스에 대한 SSL/TLS 설정 구성", + "targetTlsSettingsAdvanced": "고급 TLS 설정", + "targetTlsSni": "TLS 서버 이름 (SNI)", + "targetTlsSniDescription": "SNI에 사용할 TLS 서버 이름. 기본값을 사용하려면 비워 두십시오.", + "targetTlsSubmit": "설정 저장", + "targets": "대상 구성", + "targetsDescription": "서비스로 트래픽을 라우팅할 대상을 설정하십시오", + "targetStickySessions": "스티키 세션 활성화", + "targetStickySessionsDescription": "세션 전체 동안 동일한 백엔드 대상을 유지합니다.", + "methodSelect": "선택 방법", + "targetSubmit": "대상 추가", + "targetNoOne": "대상이 없습니다. 양식을 사용하여 대상을 추가하세요.", + "targetNoOneDescription": "위에 하나 이상의 대상을 추가하면 로드 밸런싱이 활성화됩니다.", + "targetsSubmit": "대상 저장", + "proxyAdditional": "추가 프록시 설정", + "proxyAdditionalDescription": "리소스가 프록시 설정을 처리하는 방법 구성", + "proxyCustomHeader": "사용자 정의 호스트 헤더", + "proxyCustomHeaderDescription": "요청을 프록시할 때 설정할 호스트 헤더입니다. 기본값을 사용하려면 비워 두십시오.", + "proxyAdditionalSubmit": "프록시 설정 저장", + "subnetMaskErrorInvalid": "유효하지 않은 서브넷 마스크입니다. 0에서 32 사이여야 합니다.", + "ipAddressErrorInvalidFormat": "잘못된 IP 주소 형식", + "ipAddressErrorInvalidOctet": "유효하지 않은 IP 주소 옥텟", + "path": "경로", + "ipAddressRange": "IP 범위", + "rulesErrorFetch": "규칙을 가져오는 데 실패했습니다.", + "rulesErrorFetchDescription": "규칙을 가져오는 중 오류가 발생했습니다", + "rulesErrorDuplicate": "중복 규칙", + "rulesErrorDuplicateDescription": "이 설정을 가진 규칙이 이미 존재합니다.", + "rulesErrorInvalidIpAddressRange": "유효하지 않은 CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "유효한 CIDR 값을 입력하십시오.", + "rulesErrorInvalidUrl": "유효하지 않은 URL 경로", + "rulesErrorInvalidUrlDescription": "유효한 URL 경로 값을 입력해 주세요.", + "rulesErrorInvalidIpAddress": "유효하지 않은 IP", + "rulesErrorInvalidIpAddressDescription": "유효한 IP 주소를 입력하세요", + "rulesErrorUpdate": "규칙 업데이트에 실패했습니다.", + "rulesErrorUpdateDescription": "규칙 업데이트 중 오류가 발생했습니다.", + "rulesUpdated": "규칙 활성화", + "rulesUpdatedDescription": "규칙 평가가 업데이트되었습니다", + "rulesMatchIpAddressRangeDescription": "CIDR 형식으로 주소를 입력하세요 (예: 103.21.244.0/22)", + "rulesMatchIpAddress": "IP 주소를 입력하세요 (예: 103.21.244.12)", + "rulesMatchUrl": "URL 경로 또는 패턴을 입력하세요 (예: /api/v1/todos 또는 /api/v1/*)", + "rulesErrorInvalidPriority": "유효하지 않은 우선순위", + "rulesErrorInvalidPriorityDescription": "유효한 우선 순위를 입력하세요.", + "rulesErrorDuplicatePriority": "중복 우선순위", + "rulesErrorDuplicatePriorityDescription": "고유한 우선 순위를 입력하십시오.", + "ruleUpdated": "규칙이 업데이트되었습니다", + "ruleUpdatedDescription": "규칙이 성공적으로 업데이트되었습니다", + "ruleErrorUpdate": "작업 실패", + "ruleErrorUpdateDescription": "저장 작업 중 오류가 발생했습니다.", + "rulesPriority": "우선순위", + "rulesAction": "작업", + "rulesMatchType": "일치 유형", + "value": "값", + "rulesAbout": "규칙에 대한 정보", + "rulesAboutDescription": "규칙을 사용하면 IP 주소 또는 URL 경로를 기준으로 리소스에 대한 액세스를 제어할 수 있습니다. IP 주소 또는 URL 경로를 기준으로 액세스를 허용하거나 거부하는 규칙을 만들 수 있습니다.", + "rulesActions": "작업", + "rulesActionAlwaysAllow": "항상 허용: 모든 인증 방법 우회", + "rulesActionAlwaysDeny": "항상 거부: 모든 요청을 차단합니다. 인증을 시도할 수 없습니다.", + "rulesMatchCriteria": "일치 기준", + "rulesMatchCriteriaIpAddress": "특정 IP 주소와 일치", + "rulesMatchCriteriaIpAddressRange": "CIDR 표기법으로 IP 주소 범위를 일치시킵니다", + "rulesMatchCriteriaUrl": "URL 경로 또는 패턴 일치", + "rulesEnable": "규칙 활성화", + "rulesEnableDescription": "이 리소스에 대한 규칙 평가를 활성화하거나 비활성화합니다.", + "rulesResource": "리소스 규칙 구성", + "rulesResourceDescription": "리소스에 대한 접근을 제어하는 규칙 구성", + "ruleSubmit": "규칙 추가", + "rulesNoOne": "규칙이 없습니다. 양식을 사용하여 규칙을 추가하십시오.", + "rulesOrder": "규칙은 우선 순위에 따라 오름차순으로 평가됩니다.", + "rulesSubmit": "규칙 저장", + "resourceErrorCreate": "리소스 생성 오류", + "resourceErrorCreateDescription": "리소스를 생성하는 중 오류가 발생했습니다.", + "resourceErrorCreateMessage": "리소스 생성 오류:", + "resourceErrorCreateMessageDescription": "예기치 않은 오류가 발생했습니다.", + "sitesErrorFetch": "사이트를 가져오는 중 오류가 발생했습니다.", + "sitesErrorFetchDescription": "사이트를 가져오는 중 오류가 발생했습니다", + "domainsErrorFetch": "도메인 가져오기 오류", + "domainsErrorFetchDescription": "도메인을 가져오는 중 오류가 발생했습니다.", + "none": "없음", + "unknown": "알 수 없음", + "resources": "리소스", + "resourcesDescription": "리소스는 개인 네트워크에서 실행 중인 애플리케이션에 대한 프록시입니다. 개인 네트워크에서 HTTP/HTTPS 또는 원시 TCP/UDP 서비스에 대한 리소스를 생성하십시오. 각 리소스는 암호화된 WireGuard 터널을 통해 개인적이고 안전한 연결을 가능하게 하려면 사이트에 연결되어야 합니다.", + "resourcesWireGuardConnect": "WireGuard 암호화를 통한 안전한 연결", + "resourcesMultipleAuthenticationMethods": "다중 인증 방법 구성", + "resourcesUsersRolesAccess": "사용자 및 역할 기반 접근 제어", + "resourcesErrorUpdate": "리소스를 전환하는 데 실패했습니다.", + "resourcesErrorUpdateDescription": "리소스를 업데이트하는 동안 오류가 발생했습니다.", + "access": "접속", + "shareLink": "{resource} 공유 링크", + "resourceSelect": "리소스 선택", + "shareLinks": "공유 링크", + "share": "공유 가능한 링크", + "shareDescription2": "리소스에 대한 공유 가능한 링크를 생성하세요. 링크는 리소스에 대한 임시 또는 무제한 액세스를 제공합니다. 링크를 생성할 때 만료 기간을 설정할 수 있습니다.", + "shareEasyCreate": "생성하고 공유하기 쉬움", + "shareConfigurableExpirationDuration": "구성 가능한 만료 기간", + "shareSecureAndRevocable": "안전하고 철회 가능", + "nameMin": "이름은 최소 {len}자 이상이어야 합니다.", + "nameMax": "이름은 {len}자보다 길 수 없습니다.", + "sitesConfirmCopy": "구성을 복사했는지 확인하십시오.", + "unknownCommand": "알 수 없는 명령", + "newtErrorFetchReleases": "릴리스 정보를 가져오는 데 실패했습니다: {err}", + "newtErrorFetchLatest": "최신 릴리스를 가져오는 중 오류 발생: {err}", + "newtEndpoint": "Newt 엔드포인트", + "newtId": "뉴트 ID", + "newtSecretKey": "Newt 비밀 키", + "architecture": "아키텍처", + "sites": "사이트", + "siteWgAnyClients": "WireGuard 클라이언트를 사용하여 연결하십시오. 피어 IP를 사용하여 내부 리소스에 접근해야 합니다.", + "siteWgCompatibleAllClients": "모든 WireGuard 클라이언트와 호환", + "siteWgManualConfigurationRequired": "수동 구성이 필요합니다.", + "userErrorNotAdminOrOwner": "사용자는 관리자 또는 소유자가 아닙니다.", + "pangolinSettings": "설정 - 판골린", + "accessRoleYour": "귀하의 역할:", + "accessRoleSelect2": "역할 선택", + "accessUserSelect": "사용자를 선택하세요.", + "otpEmailEnter": "이메일을 입력하세요", + "otpEmailEnterDescription": "입력 필드에 입력한 후 Enter 키를 눌러 이메일을 추가합니다.", + "otpEmailErrorInvalid": "유효하지 않은 이메일 주소입니다. 와일드카드(*)는 전체 로컬 부분이어야 합니다.", + "otpEmailSmtpRequired": "SMTP 필요", + "otpEmailSmtpRequiredDescription": "일회성 비밀번호 인증을 사용하려면 서버에서 SMTP가 활성화되어 있어야 합니다.", + "otpEmailTitle": "일회용 비밀번호", + "otpEmailTitleDescription": "리소스 접근을 위한 이메일 기반 인증 필요", + "otpEmailWhitelist": "이메일 화이트리스트", + "otpEmailWhitelistList": "화이트리스트된 이메일", + "otpEmailWhitelistListDescription": "이 이메일 주소를 가진 사용자만 이 리소스에 접근할 수 있습니다. 그들은 이메일로 전송된 일회용 비밀번호를 입력하라는 메시지를 받게 됩니다. 도메인에서 모든 이메일 주소를 허용하기 위해 와일드카드(*@example.com)를 사용할 수 있습니다.", + "otpEmailWhitelistSave": "허용 목록 저장", + "passwordAdd": "비밀번호 추가", + "passwordRemove": "비밀번호 제거", + "pincodeAdd": "PIN 코드 추가", + "pincodeRemove": "PIN 코드 제거", + "resourceAuthMethods": "인증 방법", + "resourceAuthMethodsDescriptions": "추가 인증 방법을 통해 리소스에 대한 액세스 허용", + "resourceAuthSettingsSave": "성공적으로 저장되었습니다.", + "resourceAuthSettingsSaveDescription": "인증 설정이 저장되었습니다", + "resourceErrorAuthFetch": "데이터를 가져오는 데 실패했습니다.", + "resourceErrorAuthFetchDescription": "데이터를 가져오는 중 오류가 발생했습니다.", + "resourceErrorPasswordRemove": "리소스 비밀번호 제거 오류", + "resourceErrorPasswordRemoveDescription": "리소스 비밀번호를 제거하는 동안 오류가 발생했습니다.", + "resourceErrorPasswordSetup": "리소스 비밀번호 설정 오류", + "resourceErrorPasswordSetupDescription": "리소스 비밀번호 설정 중 오류가 발생했습니다", + "resourceErrorPincodeRemove": "리소스 핀 코드 제거 오류", + "resourceErrorPincodeRemoveDescription": "리소스 핀코드를 제거하는 중 오류가 발생했습니다.", + "resourceErrorPincodeSetup": "리소스 PIN 코드 설정 중 오류가 발생했습니다.", + "resourceErrorPincodeSetupDescription": "리소스 PIN 코드를 설정하는 동안 오류가 발생했습니다.", + "resourceErrorUsersRolesSave": "역할 설정에 실패했습니다.", + "resourceErrorUsersRolesSaveDescription": "역할 설정 중 오류가 발생했습니다.", + "resourceErrorWhitelistSave": "화이트리스트 저장에 실패했습니다.", + "resourceErrorWhitelistSaveDescription": "화이트리스트를 저장하는 동안 오류가 발생했습니다.", + "resourcePasswordSubmit": "비밀번호 보호 활성화", + "resourcePasswordProtection": "비밀번호 보호 {status}", + "resourcePasswordRemove": "리소스 비밀번호가 제거되었습니다", + "resourcePasswordRemoveDescription": "리소스 비밀번호가 성공적으로 제거되었습니다.", + "resourcePasswordSetup": "리소스 비밀번호 설정됨", + "resourcePasswordSetupDescription": "리소스 비밀번호가 성공적으로 설정되었습니다.", + "resourcePasswordSetupTitle": "비밀번호 설정", + "resourcePasswordSetupTitleDescription": "이 리소스를 보호하기 위해 비밀번호를 설정하세요.", + "resourcePincode": "PIN 코드", + "resourcePincodeSubmit": "PIN 코드 보호 활성화", + "resourcePincodeProtection": "PIN 코드 보호 {상태}", + "resourcePincodeRemove": "리소스 핀코드가 제거되었습니다.", + "resourcePincodeRemoveDescription": "리소스 비밀번호가 성공적으로 제거되었습니다.", + "resourcePincodeSetup": "리소스 PIN 코드가 설정되었습니다", + "resourcePincodeSetupDescription": "리소스 핀코드가 성공적으로 설정되었습니다", + "resourcePincodeSetupTitle": "핀코드 설정", + "resourcePincodeSetupTitleDescription": "이 리소스를 보호하기 위해 핀 코드를 설정하십시오.", + "resourceRoleDescription": "관리자는 항상 이 리소스에 접근할 수 있습니다.", + "resourceUsersRoles": "사용자 및 역할", + "resourceUsersRolesDescription": "이 리소스를 방문할 수 있는 사용자 및 역할을 구성하십시오", + "resourceUsersRolesSubmit": "사용자 및 역할 저장", + "resourceWhitelistSave": "성공적으로 저장되었습니다.", + "resourceWhitelistSaveDescription": "허용 목록 설정이 저장되었습니다.", + "ssoUse": "플랫폼 SSO 사용", + "ssoUseDescription": "기존 사용자는 이 기능이 활성화된 모든 리소스에 대해 한 번만 로그인하면 됩니다.", + "proxyErrorInvalidPort": "유효하지 않은 포트 번호", + "subdomainErrorInvalid": "잘못된 하위 도메인", + "domainErrorFetch": "도메인 가져오기 오류", + "domainErrorFetchDescription": "도메인을 가져오는 중 오류가 발생했습니다.", + "resourceErrorUpdate": "리소스 업데이트에 실패했습니다.", + "resourceErrorUpdateDescription": "리소스를 업데이트하는 동안 오류가 발생했습니다.", + "resourceUpdated": "리소스가 업데이트되었습니다.", + "resourceUpdatedDescription": "리소스가 성공적으로 업데이트되었습니다.", + "resourceErrorTransfer": "리소스 전송에 실패했습니다", + "resourceErrorTransferDescription": "리소스를 전송하는 동안 오류가 발생했습니다", + "resourceTransferred": "리소스가 전송되었습니다.", + "resourceTransferredDescription": "리소스가 성공적으로 전송되었습니다.", + "gigabytes": "{count} GB", + "resourceErrorToggle": "리소스를 전환하는 데 실패했습니다.", + "resourceErrorToggleDescription": "리소스를 업데이트하는 동안 오류가 발생했습니다.", + "resourceVisibilityTitle": "가시성", + "resourceVisibilityTitleDescription": "리소스 가시성을 완전히 활성화하거나 비활성화", + "resourceGeneral": "일반 설정", + "resourceGeneralDescription": "이 리소스에 대한 일반 설정을 구성하십시오.", + "resourceEnable": "리소스 활성화", + "resourceTransfer": "리소스 전송", + "resourceTransferDescription": "이 리소스를 다른 사이트로 전송", + "resourceTransferSubmit": "리소스 전송", + "siteDestination": "대상 사이트", + "searchSites": "사이트 검색", + "accessRoleCreate": "역할 생성", + "accessRoleCreateDescription": "사용자를 그룹화하고 권한을 관리하기 위해 새 역할을 생성하세요.", + "accessRoleCreateSubmit": "역할 생성", + "accessRoleCreated": "역할이 생성되었습니다.", + "accessRoleCreatedDescription": "역할이 성공적으로 생성되었습니다.", + "accessRoleErrorCreate": "역할 생성 실패", + "accessRoleErrorCreateDescription": "역할 생성 중 오류가 발생했습니다.", + "accessRoleErrorNewRequired": "새 역할이 필요합니다.", + "accessRoleErrorRemove": "역할 제거에 실패했습니다.", + "accessRoleErrorRemoveDescription": "역할을 제거하는 동안 오류가 발생했습니다.", + "accessRoleName": "역할 이름", + "accessRoleQuestionRemove": "{name} 역할을 삭제하려고 합니다. 이 작업은 취소할 수 없습니다.", + "accessRoleRemove": "역할 제거", + "accessRoleRemoveDescription": "조직에서 역할 제거", + "accessRoleRemoveSubmit": "역할 제거", + "accessRoleRemoved": "역할이 제거되었습니다", + "accessRoleRemovedDescription": "역할이 성공적으로 제거되었습니다.", + "accessRoleRequiredRemove": "이 역할을 삭제하기 전에 기존 구성원을 전송할 새 역할을 선택하세요.", + "manage": "관리", + "sitesNotFound": "사이트를 찾을 수 없습니다.", + "pangolinServerAdmin": "서버 관리자 - 판골린", + "licenseTierProfessional": "전문 라이센스", + "licenseTierEnterprise": "기업 라이선스", + "licenseTierCommercial": "상업용 라이선스", + "licensed": "라이센스", + "yes": "예", + "no": "아니요", + "sitesAdditional": "추가 사이트", + "licenseKeys": "라이센스 키", + "sitestCountDecrease": "사이트 수 줄이기", + "sitestCountIncrease": "사이트 수 증가", + "idpManage": "아이덴티티 공급자 관리", + "idpManageDescription": "시스템에서 ID 제공자를 보고 관리합니다", + "idpDeletedDescription": "신원 공급자가 성공적으로 삭제되었습니다", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "정말로 아이덴티티 공급자 {name}를 영구적으로 삭제하시겠습니까?", + "idpMessageRemove": "이 작업은 아이덴티티 공급자와 모든 관련 구성을 제거합니다. 이 공급자를 통해 인증하는 사용자는 더 이상 로그인할 수 없습니다.", + "idpMessageConfirm": "확인을 위해 아래에 아이덴티티 제공자의 이름을 입력하세요.", + "idpConfirmDelete": "신원 제공자 삭제 확인", + "idpDelete": "아이덴티티 공급자 삭제", + "idp": "신원 공급자", + "idpSearch": "ID 공급자 검색...", + "idpAdd": "아이덴티티 공급자 추가", + "idpClientIdRequired": "클라이언트 ID가 필요합니다.", + "idpClientSecretRequired": "클라이언트 비밀이 필요합니다.", + "idpErrorAuthUrlInvalid": "인증 URL은 유효한 URL이어야 합니다.", + "idpErrorTokenUrlInvalid": "토큰 URL은 유효한 URL이어야 합니다.", + "idpPathRequired": "식별자 경로가 필요합니다.", + "idpScopeRequired": "범위가 필요합니다.", + "idpOidcDescription": "OpenID Connect ID 공급자를 구성하십시오.", + "idpCreatedDescription": "ID 공급자가 성공적으로 생성되었습니다.", + "idpCreate": "아이덴티티 공급자 생성", + "idpCreateDescription": "사용자 인증을 위한 새로운 ID 공급자를 구성합니다.", + "idpSeeAll": "모든 ID 공급자 보기", + "idpSettingsDescription": "신원 제공자의 기본 정보를 구성하세요", + "idpDisplayName": "이 신원 공급자를 위한 표시 이름", + "idpAutoProvisionUsers": "사용자 자동 프로비저닝", + "idpAutoProvisionUsersDescription": "활성화되면 사용자가 첫 로그인 시 시스템에 자동으로 생성되며, 사용자와 역할 및 조직을 매핑할 수 있습니다.", + "licenseBadge": "전문가", + "idpType": "제공자 유형", + "idpTypeDescription": "구성할 ID 공급자의 유형을 선택하십시오.", + "idpOidcConfigure": "OAuth2/OIDC 구성", + "idpOidcConfigureDescription": "OAuth2/OIDC 공급자 엔드포인트 및 자격 증명을 구성하십시오.", + "idpClientId": "클라이언트 ID", + "idpClientIdDescription": "아이덴티티 공급자의 OAuth2 클라이언트 ID", + "idpClientSecret": "클라이언트 비밀", + "idpClientSecretDescription": "신원 제공자로부터의 OAuth2 클라이언트 비밀", + "idpAuthUrl": "인증 URL", + "idpAuthUrlDescription": "OAuth2 인증 엔드포인트 URL", + "idpTokenUrl": "토큰 URL", + "idpTokenUrlDescription": "OAuth2 토큰 엔드포인트 URL", + "idpOidcConfigureAlert": "중요 정보", + "idpOidcConfigureAlertDescription": "아이덴티티 공급자를 생성한 후, 아이덴티티 공급자의 설정에서 콜백 URL을 구성해야 합니다. 콜백 URL은 성공적으로 생성된 후 제공됩니다.", + "idpToken": "토큰 구성", + "idpTokenDescription": "ID 토큰에서 사용자 정보를 추출하는 방법 구성", + "idpJmespathAbout": "JMESPath에 대하여", + "idpJmespathAboutDescription": "아래 경로는 ID 토큰에서 값을 추출하기 위해 JMESPath 구문을 사용합니다.", + "idpJmespathAboutDescriptionLink": "JMESPath에 대해 더 알아보기", + "idpJmespathLabel": "식별자 경로", + "idpJmespathLabelDescription": "ID 토큰에서 사용자 식별자에 대한 경로", + "idpJmespathEmailPathOptional": "이메일 경로 (선택 사항)", + "idpJmespathEmailPathOptionalDescription": "ID 토큰에서 사용자의 이메일 경로", + "idpJmespathNamePathOptional": "이름 경로 (선택 사항)", + "idpJmespathNamePathOptionalDescription": "ID 토큰에서 사용자의 이름 경로", + "idpOidcConfigureScopes": "범위", + "idpOidcConfigureScopesDescription": "요청할 OAuth2 범위의 공백으로 구분된 목록", + "idpSubmit": "아이덴티티 공급자 생성", + "orgPolicies": "조직 정책", + "idpSettings": "{idpName} 설정", + "megabytes": "{count} MB", + "actionCheckOrgId": "ID 확인", + "idpCreateSettingsDescription": "아이덴티티 공급자의 설정을 구성하십시오", + "roleMapping": "역할 매핑", + "orgMapping": "조직 매핑", + "orgPoliciesSearch": "조직 정책 검색...", + "orgPoliciesAdd": "조직 정책 추가", + "orgRequired": "조직은 필수입니다.", + "error": "오류", + "success": "성공", + "orgPolicyAddedDescription": "정책이 성공적으로 추가되었습니다", + "orgPolicyUpdatedDescription": "정책이 성공적으로 업데이트되었습니다.", + "tagsEntered": "입력된 태그", + "defaultMappingsOptionalDescription": "조직에 대해 정의된 정책이 없을 때 기본 매핑이 사용됩니다. 여기에서 기본 역할 및 조직 매핑을 지정하여 대체할 수 있습니다.", + "defaultMappingsRole": "기본 역할 매핑", + "defaultMappingsRoleDescription": "이 표현식의 결과는 조직에서 정의된 역할 이름을 문자열로 반환해야 합니다.", + "defaultMappingsOrg": "기본 조직 매핑", + "defaultMappingsOrgDescription": "이 표현식은 사용자가 조직에 접근할 수 있도록 조직 ID 또는 true를 반환해야 합니다.", + "defaultMappingsSubmit": "기본 매핑 저장", + "orgPoliciesEdit": "조직 정책 편집", + "org": "조직", + "orgSelect": "조직 선택", + "orgSearch": "조직 검색", + "orgNotFound": "조직을 찾을 수 없습니다.", + "roleMappingPathOptional": "역할 매핑 경로 (선택 사항)", + "orgMappingPathOptional": "조직 매핑 경로 (선택 사항)", + "orgPolicyUpdate": "정책 업데이트", + "orgPolicyAdd": "정책 추가", + "orgPolicyConfig": "조직에 대한 접근을 구성하십시오.", + "idpUpdatedDescription": "아이덴티티 제공자가 성공적으로 업데이트되었습니다", + "redirectUrl": "리디렉션 URL", + "redirectUrlAbout": "리디렉션 URL에 대한 정보", + "redirectUrlAboutDescription": "사용자가 인증 후 리디렉션될 URL입니다. 이 URL을 신원 제공자 설정에서 구성해야 합니다.", + "pangolinAuth": "인증 - 판골린", + "verificationCodeLengthRequirements": "인증 코드가 8자여야 합니다.", + "errorOccurred": "오류가 발생했습니다.", + "emailErrorVerify": "이메일 확인에 실패했습니다:", + "emailVerified": "이메일이 성공적으로 확인되었습니다! 리디렉션 중입니다...", + "verificationCodeErrorResend": "인증 코드를 재전송하는 데 실패했습니다:", + "verificationCodeResend": "인증 코드가 재전송되었습니다", + "verificationCodeResendDescription": "검증 코드를 귀하의 이메일 주소로 재전송했습니다. 받은 편지함을 확인해 주세요.", + "emailVerify": "이메일 확인", + "emailVerifyDescription": "이메일 주소로 전송된 인증 코드를 입력하세요.", + "verificationCode": "인증 코드", + "verificationCodeEmailSent": "귀하의 이메일 주소로 인증 코드가 전송되었습니다.", + "submit": "제출", + "emailVerifyResendProgress": "재전송 중...", + "emailVerifyResend": "코드를 받지 못하셨나요? 여기 클릭하여 재전송하세요", + "passwordNotMatch": "비밀번호가 일치하지 않습니다.", + "resourceNotFoundDescription": "접근하려는 리소스가 존재하지 않습니다.", + "pincodeRequirementsLength": "PIN은 정확히 6자리여야 합니다", + "pincodeRequirementsChars": "PIN은 숫자만 포함해야 합니다.", + "passwordRequirementsLength": "비밀번호는 최소 1자 이상이어야 합니다", + "otpEmailRequirementsLength": "OTP는 최소 1자 이상이어야 합니다", + "otpEmailSent": "OTP 전송됨", + "otpEmailSentDescription": "OTP가 귀하의 이메일로 전송되었습니다.", + "otpEmailErrorAuthenticate": "이메일로 인증에 실패했습니다", + "pincodeErrorAuthenticate": "핀코드로 인증하는 데 실패했습니다", + "passwordErrorAuthenticate": "비밀번호로 인증하는 데 실패했습니다.", + "poweredBy": "제공자", + "authenticationRequired": "인증 필요", + "authenticationMethodChoose": "{name}에 접근하기 위한 선호하는 방법을 선택하세요.", + "authenticationRequest": "{name}에 접근하려면 인증해야 합니다.", + "user": "사용자", + "pincodeInput": "6자리 PIN 코드", + "pincodeSubmit": "PIN으로 로그인", + "passwordSubmit": "비밀번호로 로그인", + "otpEmailDescription": "일회성 코드가 이 이메일로 전송됩니다.", + "otpEmailSend": "일회성 코드 전송", + "otpEmail": "일회성 비밀번호 (OTP)", + "otpEmailSubmit": "OTP 제출", + "backToEmail": "이메일로 돌아가기", + "noSupportKey": "서버가 지원 키 없이 실행되고 있습니다. 프로젝트 지원을 고려하세요!", + "accessDenied": "접근 거부", + "accessDeniedDescription": "이 리소스에 접근할 수 있는 권한이 없습니다. 이게 실수라면 관리자에게 문의해 주세요.", + "accessTokenError": "액세스 토큰 확인 중 오류 발생", + "accessGranted": "접근 허가됨", + "accessUrlInvalid": "접근 URL이 유효하지 않습니다", + "accessGrantedDescription": "이 리소스에 대한 접근이 허용되었습니다. 리디렉션 중입니다...", + "accessUrlInvalidDescription": "이 공유 액세스 URL은 유효하지 않습니다. 새로운 URL을 위해 리소스 소유자에게 문의하세요.", + "tokenInvalid": "유효하지 않은 토큰", + "pincodeInvalid": "유효하지 않은 코드", + "passwordErrorRequestReset": "재설정을 요청하는 데 실패했습니다:", + "passwordErrorReset": "비밀번호 재설정 실패:", + "passwordResetSuccess": "비밀번호가 성공적으로 재설정되었습니다! 로그인으로 돌아가기...", + "passwordReset": "비밀번호 재설정", + "passwordResetDescription": "비밀번호를 재설정하는 단계를 따르세요", + "passwordResetSent": "이 이메일 주소로 비밀번호 재설정 코드를 전송하겠습니다.", + "passwordResetCode": "코드 재설정", + "passwordResetCodeDescription": "재설정 코드를 확인하려면 이메일을 확인하세요.", + "passwordNew": "새 비밀번호", + "passwordNewConfirm": "새 비밀번호 확인", + "pincodeAuth": "인증 코드", + "pincodeSubmit2": "코드 제출", + "passwordResetSubmit": "재설정 요청", + "passwordBack": "비밀번호로 돌아가기", + "loginBack": "로그인으로 돌아가기", + "signup": "가입하기", + "loginStart": "시작하려면 로그인하세요.", + "idpOidcTokenValidating": "OIDC 토큰 검증 중", + "idpOidcTokenResponse": "OIDC 토큰 응답 검증", + "idpErrorOidcTokenValidating": "OIDC 토큰 검증 오류", + "idpConnectingTo": "{name}에 연결 중", + "idpConnectingToDescription": "귀하의 신원을 확인하는 중", + "idpConnectingToProcess": "연결 중...", + "idpConnectingToFinished": "연결됨", + "idpErrorConnectingTo": "{name}에 연결하는 데 문제가 발생했습니다. 관리자에게 문의하십시오.", + "idpErrorNotFound": "IdP를 찾을 수 없습니다.", + "inviteInvalid": "유효하지 않은 초대", + "inviteInvalidDescription": "초대 링크가 유효하지 않습니다.", + "inviteErrorWrongUser": "이 초대는 이 사용자에게 해당되지 않습니다", + "inviteErrorUserNotExists": "사용자가 존재하지 않습니다. 먼저 계정을 생성해 주세요.", + "inviteErrorLoginRequired": "초대를 수락하려면 로그인해야 합니다.", + "inviteErrorExpired": "초대가 만료되었을 수 있습니다.", + "inviteErrorRevoked": "초대가 취소되었을 수 있습니다.", + "inviteErrorTypo": "초대 링크에 오타가 있을 수 있습니다.", + "pangolinSetup": "설정 - 판골린", + "orgNameRequired": "조직 이름은 필수입니다.", + "orgIdRequired": "조직 ID가 필요합니다", + "orgErrorCreate": "조직 생성 중 오류가 발생했습니다.", + "pageNotFound": "페이지를 찾을 수 없습니다", + "pageNotFoundDescription": "앗! 찾고 있는 페이지가 존재하지 않습니다.", + "overview": "개요", + "home": "홈", + "accessControl": "액세스 제어", + "settings": "설정", + "usersAll": "모든 사용자", + "license": "라이선스", + "pangolinDashboard": "대시보드 - 판골린", + "tagsWarnCannotBeLessThanZero": "maxTags와 minTags는 0보다 작을 수 없습니다", + "tagsWarnNotAllowedAutocompleteOptions": "자동 완성 옵션에 따라 태그가 허용되지 않습니다", + "tagsWarnInvalid": "validateTag에 따라 유효하지 않은 태그입니다", + "tagWarnTooShort": "태그 {tagText}가 너무 짧습니다", + "tagWarnTooLong": "태그 {tagText}가 너무 깁니다.", + "tagsWarnReachedMaxNumber": "허용된 최대 태그 수에 도달했습니다.", + "tagWarnDuplicate": "중복 태그 {tagText}가 추가되지 않았습니다.", + "supportKeyInvalid": "유효하지 않은 키", + "supportKeyInvalidDescription": "지원자 키가 유효하지 않습니다.", + "supportKeyValid": "유효한 키", + "supportKeyValidDescription": "귀하의 후원자 키가 검증되었습니다. 지원해 주셔서 감사합니다!", + "supportKeyErrorValidationDescription": "서포터 키 유효성 검사에 실패했습니다.", + "supportKey": "개발 지원 및 판골린을 입양하세요!", + "supportKeyDescription": "커뮤니티를 위해 Pangolin 개발을 지속할 수 있도록 후원자 키를 구매하세요. 귀하의 기여는 모든 사용자를 위해 애플리케이션을 유지하고 새로운 기능을 추가하는 데 더 많은 시간을 할애할 수 있게 해줍니다. 우리는 절대 이 기능을 유료화하는 데 사용하지 않을 것입니다. 이는 상업용 에디션과는 별개입니다.", + "supportKeyPet": "자신만의 애완 판골린을 입양하고 만날 수 있습니다!", + "supportKeyPurchase": "결제는 GitHub를 통해 처리됩니다. 이후, 키를 다음에서 검색할 수 있습니다.", + "supportKeyPurchaseLink": "우리 웹사이트", + "supportKeyPurchase2": "여기에서 사용하세요.", + "supportKeyLearnMore": "자세히 알아보기.", + "supportKeyOptions": "가장 적합한 옵션을 선택해 주세요.", + "supportKetOptionFull": "전체 후원자", + "forWholeServer": "전체 서버에 대해", + "lifetimePurchase": "평생 구매", + "supporterStatus": "후원자 상태", + "buy": "구매", + "supportKeyOptionLimited": "제한된 후원자", + "forFiveUsers": "5명 이하의 사용자에 대해", + "supportKeyRedeem": "서포터 키 사용", + "supportKeyHideSevenDays": "7일 동안 숨기기", + "supportKeyEnter": "지원자 키 입력", + "supportKeyEnterDescription": "당신만의 펭귄 애완동물을 만나보세요!", + "githubUsername": "GitHub 사용자 이름", + "supportKeyInput": "후원자 키", + "supportKeyBuy": "서포터 키 구매", + "logoutError": "로그아웃 중 오류 발생", + "signingAs": "로그인한 사용자", + "serverAdmin": "서버 관리자", + "licenseTierProfessionalRequired": "전문 에디션이 필요합니다.", + "licenseTierProfessionalRequiredDescription": "이 기능은 Professional Edition에서만 사용할 수 있습니다.", + "actionGetOrg": "조직 가져오기", + "actionUpdateOrg": "조직 업데이트", + "actionGetOrgUser": "조직 사용자 가져오기", + "actionListOrgDomains": "조직 도메인 목록", + "actionCreateSite": "사이트 생성", + "actionDeleteSite": "사이트 삭제", + "actionGetSite": "사이트 가져오기", + "actionListSites": "사이트 목록", + "actionUpdateSite": "사이트 업데이트", + "actionListSiteRoles": "허용된 사이트 역할 목록", + "actionCreateResource": "리소스 생성", + "actionDeleteResource": "리소스 삭제", + "actionGetResource": "리소스 가져오기", + "actionListResource": "리소스 목록", + "actionUpdateResource": "리소스 업데이트", + "actionListResourceUsers": "리소스 사용자 목록", + "actionSetResourceUsers": "리소스 사용자 설정", + "actionSetAllowedResourceRoles": "허용된 리소스 역할 설정", + "actionListAllowedResourceRoles": "허용된 리소스 역할 목록", + "actionSetResourcePassword": "리소스 비밀번호 설정", + "actionSetResourcePincode": "리소스 핀코드 설정", + "actionSetResourceEmailWhitelist": "리소스 이메일 화이트리스트 설정", + "actionGetResourceEmailWhitelist": "리소스 이메일 화이트리스트 가져오기", + "actionCreateTarget": "대상 만들기", + "actionDeleteTarget": "대상 삭제", + "actionGetTarget": "대상 가져오기", + "actionListTargets": "대상 목록", + "actionUpdateTarget": "대상 업데이트", + "actionCreateRole": "역할 생성", + "actionDeleteRole": "역할 삭제", + "actionGetRole": "역할 가져오기", + "actionListRole": "역할 목록", + "actionUpdateRole": "역할 업데이트", + "actionListAllowedRoleResources": "허용된 역할 리소스 목록", + "actionInviteUser": "사용자 초대", + "actionRemoveUser": "사용자 제거", + "actionListUsers": "사용자 목록", + "actionAddUserRole": "사용자 역할 추가", + "containersIn": "{siteName}의 컨테이너", + "actionGenerateAccessToken": "액세스 토큰 생성", + "actionDeleteAccessToken": "액세스 토큰 삭제", + "actionListAccessTokens": "액세스 토큰 목록", + "actionListResourceRules": "리소스 규칙 목록", + "actionUpdateResourceRule": "리소스 규칙 업데이트", + "actionListOrgs": "조직 목록", + "actionCreateOrg": "조직 생성", + "actionDeleteOrg": "조직 삭제", + "actionListApiKeys": "API 키 목록", + "actionListApiKeyActions": "API 키 작업 목록", + "actionSetApiKeyActions": "API 키 허용 작업 설정", + "actionCreateApiKey": "API 키 생성", + "actionDeleteApiKey": "API 키 삭제", + "actionCreateIdp": "IDP 생성", + "actionUpdateIdp": "IDP 업데이트", + "actionDeleteIdp": "IDP 삭제", + "actionListIdps": "IDP 목록", + "actionGetIdp": "IDP 가져오기", + "actionCreateIdpOrg": "IDP 조직 정책 생성", + "actionDeleteIdpOrg": "IDP 조직 정책 삭제", + "actionListIdpOrgs": "IDP 조직 목록", + "actionUpdateIdpOrg": "IDP 조직 업데이트", + "noneSelected": "선택된 항목 없음", + "orgNotFound2": "조직이 없습니다.", + "searchProgress": "검색...", + "create": "생성", + "orgs": "조직", + "loginError": "로그인 중 오류가 발생했습니다", + "passwordForgot": "비밀번호를 잊으셨나요?", + "otpAuth": "이중 인증", + "otpAuthDescription": "인증 앱에서 코드를 입력하거나 단일 사용 백업 코드 중 하나를 입력하세요.", + "otpAuthSubmit": "코드 제출", + "idpContinue": "또는 계속 진행하십시오.", + "otpAuthBack": "로그인으로 돌아가기", + "navbar": "탐색 메뉴", + "navbarDescription": "애플리케이션의 주요 탐색 메뉴", + "navbarDocsLink": "문서", + "commercialEdition": "상업용 에디션", + "otpErrorEnable": "2FA를 활성화할 수 없습니다.", + "otpErrorEnableDescription": "2FA를 활성화하는 동안 오류가 발생했습니다", + "otpSetupCheckCode": "6자리 코드를 입력하세요", + "otpSetupCheckCodeRetry": "유효하지 않은 코드입니다. 다시 시도하세요.", + "otpSetup": "이중 인증 활성화", + "otpSetupDescription": "추가 보호 계층으로 계정을 안전하게 유지하세요.", + "otpSetupScanQr": "인증 앱으로 이 QR 코드를 스캔하거나 비밀 키를 수동으로 입력하십시오:", + "otpSetupSecretCode": "인증 코드", + "otpSetupSuccess": "이중 인증 활성화됨", + "otpSetupSuccessStoreBackupCodes": "귀하의 계정이 이제 더 안전해졌습니다. 백업 코드를 저장하는 것을 잊지 마세요.", + "otpErrorDisable": "2FA를 비활성화할 수 없습니다.", + "otpErrorDisableDescription": "2FA를 비활성화하는 동안 오류가 발생했습니다.", + "otpRemove": "이중 인증 비활성화", + "otpRemoveDescription": "계정에 대한 이중 인증 비활성화", + "otpRemoveSuccess": "이중 인증 비활성화", + "otpRemoveSuccessMessage": "이중 인증이 귀하의 계정에서 비활성화되었습니다. 언제든지 다시 활성화할 수 있습니다.", + "otpRemoveSubmit": "2FA 비활성화", + "paginator": "페이지 {current} / {last}", + "paginatorToFirst": "첫 페이지로 이동", + "paginatorToPrevious": "이전 페이지로 이동", + "paginatorToNext": "다음 페이지로 이동", + "paginatorToLast": "마지막 페이지로 이동", + "copyText": "텍스트 복사", + "copyTextFailed": "텍스트 복사 실패: ", + "copyTextClipboard": "클립보드에 복사", + "inviteErrorInvalidConfirmation": "유효하지 않은 확인", + "passwordRequired": "비밀번호는 필수입니다.", + "allowAll": "모두 허용", + "permissionsAllowAll": "모든 권한 허용", + "githubUsernameRequired": "GitHub 사용자 이름이 필요합니다.", + "supportKeyRequired": "지원자 키가 필요합니다.", + "passwordRequirementsChars": "비밀번호는 최소 8자 이상이어야 합니다", + "language": "언어", + "verificationCodeRequired": "코드가 필요합니다.", + "userErrorNoUpdate": "업데이트할 사용자가 없습니다", + "siteErrorNoUpdate": "업데이트할 사이트가 없습니다.", + "resourceErrorNoUpdate": "업데이트할 리소스가 없습니다", + "authErrorNoUpdate": "업데이트할 인증 정보가 없습니다.", + "orgErrorNoUpdate": "업데이트할 조직이 없습니다.", + "orgErrorNoProvided": "제공된 조직이 없습니다.", + "apiKeysErrorNoUpdate": "업데이트할 API 키가 없습니다.", + "sidebarOverview": "개요", + "sidebarHome": "홈", + "sidebarSites": "사이트", + "sidebarResources": "리소스", + "sidebarAccessControl": "액세스 제어", + "sidebarUsers": "사용자", + "sidebarInvitations": "초대", + "sidebarRoles": "역할", + "sidebarShareableLinks": "공유 가능한 링크", + "sidebarIdentityProviders": "신원 공급자", + "sidebarLicense": "라이선스", + "enableDockerSocket": "Docker 소켓 활성화", + "enableDockerSocketDescription": "컨테이너 정보를 채우기 위해 Docker 소켓 검색을 활성화합니다. 소켓 경로는 Newt에 제공되어야 합니다.", + "enableDockerSocketLink": "자세히 알아보기", + "viewDockerContainers": "도커 컨테이너 보기", + "selectContainerDescription": "이 대상을 위한 호스트 이름으로 사용할 컨테이너를 선택하세요. 포트를 사용하려면 포트를 클릭하세요.", + "containerName": "이름", + "containerImage": "이미지", + "containerState": "주", + "containerNetworks": "네트워크", + "containerHostnameIp": "호스트 이름/IP", + "containerLabels": "레이블", + "containerLabelsCount": "{count, plural, one {# 레이블} other {# 레이블}}", + "containerLabelsTitle": "컨테이너 레이블", + "containerLabelEmpty": "<비어 있음>", + "containerPorts": "포트", + "containerPortsMore": "+{count}개 더", + "containerActions": "작업", + "select": "선택", + "noContainersMatchingFilters": "현재 필터와 일치하는 컨테이너를 찾을 수 없습니다.", + "showContainersWithoutPorts": "포트가 없는 컨테이너 표시", + "showStoppedContainers": "중지된 컨테이너 표시", + "noContainersFound": "컨테이너를 찾을 수 없습니다. Docker 컨테이너가 실행 중인지 확인하십시오.", + "searchContainersPlaceholder": "{count}개의 컨테이너에서 검색...", + "searchResultsCount": "{count, plural, one {# 결과} other {# 결과}}", + "filters": "필터", + "filterOptions": "필터 옵션", + "filterPorts": "포트", + "filterStopped": "중지됨", + "clearAllFilters": "모든 필터 지우기", + "columns": "열", + "toggleColumns": "열 전환", + "refreshContainersList": "컨테이너 목록 새로 고침", + "searching": "검색 중...", + "noContainersFoundMatching": "\"{filter}\"와 일치하는 컨테이너를 찾을 수 없습니다.", + "light": "빛", + "dark": "어두운", + "system": "시스템", + "theme": "테마", + "initialSetupTitle": "초기 서버 설정", + "initialSetupDescription": "초기 서버 관리자 계정을 생성하세요. 서버 관리자 계정은 하나만 존재할 수 있습니다. 이러한 자격 증명은 나중에 언제든지 변경할 수 있습니다.", + "createAdminAccount": "관리자 계정 생성", + "setupErrorCreateAdmin": "서버 관리자 계정을 생성하는 동안 오류가 발생했습니다." +} diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 7e625b00..39aaa9b6 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Initiële serverconfiguratie", "initialSetupDescription": "Maak het eerste serverbeheeraccount aan. Er kan slechts één serverbeheerder bestaan. U kunt deze inloggegevens later altijd wijzigen.", "createAdminAccount": "Maak een beheeraccount aan", - "setupErrorCreateAdmin": "Er is een fout opgetreden bij het maken van het serverbeheerdersaccount." + "setupErrorCreateAdmin": "Er is een fout opgetreden bij het maken van het serverbeheerdersaccount.", + "securityKeyManage": "Beveiligingssleutels beheren", + "securityKeyDescription": "Voeg beveiligingssleutels toe of verwijder ze voor wachtwoordloze authenticatie", + "securityKeyRegister": "Nieuwe beveiligingssleutel registreren", + "securityKeyList": "Uw beveiligingssleutels", + "securityKeyNone": "Nog geen beveiligingssleutels geregistreerd", + "securityKeyNameRequired": "Naam is verplicht", + "securityKeyRemove": "Verwijderen", + "securityKeyLastUsed": "Laatst gebruikt: {date}", + "securityKeyNameLabel": "Naam", + "securityKeyNamePlaceholder": "Voer een naam in voor deze beveiligingssleutel", + "securityKeyRegisterSuccess": "Beveiligingssleutel succesvol geregistreerd", + "securityKeyRegisterError": "Fout bij registreren van beveiligingssleutel", + "securityKeyRemoveSuccess": "Beveiligingssleutel succesvol verwijderd", + "securityKeyRemoveError": "Fout bij verwijderen van beveiligingssleutel", + "securityKeyLoadError": "Fout bij laden van beveiligingssleutels", + "securityKeyLogin": "Inloggen met beveiligingssleutel", + "securityKeyAuthError": "Fout bij authenticatie met beveiligingssleutel", + "securityKeyRecommendation": "Overweeg om een andere beveiligingssleutel te registreren op een ander apparaat om ervoor te zorgen dat u niet buitengesloten raakt van uw account." } diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 25cf2e6a..e21902ea 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Wstępna konfiguracja serwera", "initialSetupDescription": "Utwórz początkowe konto administratora serwera. Może istnieć tylko jeden administrator serwera. Zawsze można zmienić te dane uwierzytelniające.", "createAdminAccount": "Utwórz konto administratora", - "setupErrorCreateAdmin": "Wystąpił błąd podczas tworzenia konta administratora serwera." + "setupErrorCreateAdmin": "Wystąpił błąd podczas tworzenia konta administratora serwera.", + "securityKeyManage": "Zarządzaj kluczami bezpieczeństwa", + "securityKeyDescription": "Dodaj lub usuń klucze bezpieczeństwa do uwierzytelniania bez hasła", + "securityKeyRegister": "Zarejestruj nowy klucz bezpieczeństwa", + "securityKeyList": "Twoje klucze bezpieczeństwa", + "securityKeyNone": "Brak zarejestrowanych kluczy bezpieczeństwa", + "securityKeyNameRequired": "Nazwa jest wymagana", + "securityKeyRemove": "Usuń", + "securityKeyLastUsed": "Ostatnio używany: {date}", + "securityKeyNameLabel": "Nazwa", + "securityKeyNamePlaceholder": "Wprowadź nazwę dla tego klucza bezpieczeństwa", + "securityKeyRegisterSuccess": "Klucz bezpieczeństwa został pomyślnie zarejestrowany", + "securityKeyRegisterError": "Błąd podczas rejestracji klucza bezpieczeństwa", + "securityKeyRemoveSuccess": "Klucz bezpieczeństwa został pomyślnie usunięty", + "securityKeyRemoveError": "Błąd podczas usuwania klucza bezpieczeństwa", + "securityKeyLoadError": "Błąd podczas ładowania kluczy bezpieczeństwa", + "securityKeyLogin": "Zaloguj się kluczem bezpieczeństwa", + "securityKeyAuthError": "Błąd podczas uwierzytelniania kluczem bezpieczeństwa", + "securityKeyRecommendation": "Rozważ zarejestrowanie innego klucza bezpieczeństwa na innym urządzeniu, aby upewnić się, że nie zostaniesz zablokowany z dostępu do swojego konta." } diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 69e650d5..1d1b9ba1 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "Configuração Inicial do Servidor", "initialSetupDescription": "Crie a conta de administrador inicial do servidor. Apenas um administrador do servidor pode existir. Você sempre pode alterar essas credenciais posteriormente.", "createAdminAccount": "Criar Conta de Administrador", - "setupErrorCreateAdmin": "Ocorreu um erro ao criar a conta de administrador do servidor." + "setupErrorCreateAdmin": "Ocorreu um erro ao criar a conta de administrador do servidor.", + "securityKeyManage": "Gerenciar chaves de segurança", + "securityKeyDescription": "Adicionar ou remover chaves de segurança para autenticação sem senha", + "securityKeyRegister": "Registrar nova chave de segurança", + "securityKeyList": "Suas chaves de segurança", + "securityKeyNone": "Nenhuma chave de segurança registrada", + "securityKeyNameRequired": "Nome é obrigatório", + "securityKeyRemove": "Remover", + "securityKeyLastUsed": "Último uso: {date}", + "securityKeyNameLabel": "Nome", + "securityKeyNamePlaceholder": "Digite um nome para esta chave de segurança", + "securityKeyRegisterSuccess": "Chave de segurança registrada com sucesso", + "securityKeyRegisterError": "Erro ao registrar chave de segurança", + "securityKeyRemoveSuccess": "Chave de segurança removida com sucesso", + "securityKeyRemoveError": "Erro ao remover chave de segurança", + "securityKeyLoadError": "Erro ao carregar chaves de segurança", + "securityKeyLogin": "Entrar com chave de segurança", + "securityKeyAuthError": "Erro ao autenticar com chave de segurança", + "securityKeyRecommendation": "Considere registrar outra chave de segurança em um dispositivo diferente para garantir que você não fique bloqueado da sua conta." } diff --git a/messages/tr-TR.json b/messages/tr-TR.json index ad6a0fe3..085505b4 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -1132,5 +1132,23 @@ "initialSetupTitle": "İlk Sunucu Kurulumu", "initialSetupDescription": "İlk sunucu yönetici hesabını oluşturun. Yalnızca bir sunucu yöneticisi olabilir. Bu kimlik bilgilerini daha sonra her zaman değiştirebilirsiniz.", "createAdminAccount": "Yönetici Hesabı Oluştur", - "setupErrorCreateAdmin": "Sunucu yönetici hesabı oluşturulurken bir hata oluştu." + "setupErrorCreateAdmin": "Sunucu yönetici hesabı oluşturulurken bir hata oluştu.", + "securityKeyManage": "Güvenlik Anahtarlarını Yönet", + "securityKeyDescription": "Şifresiz kimlik doğrulama için güvenlik anahtarları ekleyin veya kaldırın", + "securityKeyRegister": "Yeni Güvenlik Anahtarı Kaydet", + "securityKeyList": "Güvenlik Anahtarlarınız", + "securityKeyNone": "Henüz kayıtlı güvenlik anahtarı yok", + "securityKeyNameRequired": "İsim gerekli", + "securityKeyRemove": "Kaldır", + "securityKeyLastUsed": "Son kullanım: {date}", + "securityKeyNameLabel": "İsim", + "securityKeyNamePlaceholder": "Bu güvenlik anahtarı için bir isim girin", + "securityKeyRegisterSuccess": "Güvenlik anahtarı başarıyla kaydedildi", + "securityKeyRegisterError": "Güvenlik anahtarı kaydedilirken hata oluştu", + "securityKeyRemoveSuccess": "Güvenlik anahtarı başarıyla kaldırıldı", + "securityKeyRemoveError": "Güvenlik anahtarı kaldırılırken hata oluştu", + "securityKeyLoadError": "Güvenlik anahtarları yüklenirken hata oluştu", + "securityKeyLogin": "Güvenlik anahtarı ile giriş yap", + "securityKeyAuthError": "Güvenlik anahtarı ile kimlik doğrulama başarısız oldu", + "securityKeyRecommendation": "Hesabınızdan kilitlenmediğinizden emin olmak için farklı bir cihazda başka bir güvenlik anahtarı kaydetmeyi düşünün." } diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 676d5f56..8076ebda 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -212,7 +212,7 @@ "orgDeleteConfirm": "确认删除组织", "orgMessageRemove": "此操作不可逆,这将删除所有相关数据。", "orgMessageConfirm": "要确认,请在下面输入组织名称。", - "orgQuestionRemove": "你确定要删除 “{selectedOrg}” 组织吗?", + "orgQuestionRemove": "你确定要删除 \"{selectedOrg}\" 组织吗?", "orgUpdated": "组织已更新", "orgUpdatedDescription": "组织已更新。", "orgErrorUpdate": "更新组织失败", @@ -279,7 +279,7 @@ "apiKeysAdd": "生成 API 密钥", "apiKeysErrorDelete": "删除 API 密钥出错", "apiKeysErrorDeleteMessage": "删除 API 密钥出错", - "apiKeysQuestionRemove": "您确定要从组织中删除 “{selectedApiKey}” API密钥吗?", + "apiKeysQuestionRemove": "您确定要从组织中删除 \"{selectedApiKey}\" API密钥吗?", "apiKeysMessageRemove": "一旦删除,此API密钥将无法被使用。", "apiKeysMessageConfirm": "要确认,请在下方输入API密钥名称。", "apiKeysDeleteConfirm": "确认删除 API 密钥", @@ -715,7 +715,7 @@ "idpManageDescription": "查看和管理系统中的身份提供商", "idpDeletedDescription": "身份提供商删除成功", "idpOidc": "OAuth2/OIDC", - "idpQuestionRemove": "你确定要永久删除 “{name}” 这个身份提供商吗?", + "idpQuestionRemove": "你确定要永久删除 \"{name}\" 这个身份提供商吗?", "idpMessageRemove": "这将删除身份提供者和所有相关的配置。通过此提供者进行身份验证的用户将无法登录。", "idpMessageConfirm": "要确认,请在下面输入身份提供者的名称。", "idpConfirmDelete": "确认删除身份提供商", @@ -1132,5 +1132,23 @@ "initialSetupTitle": "初始服务器设置", "initialSetupDescription": "创建初始服务器管理员帐户。 只能存在一个服务器管理员。 您可以随时更改这些凭据。", "createAdminAccount": "创建管理员帐户", - "setupErrorCreateAdmin": "创建服务器管理员帐户时出错。" + "setupErrorCreateAdmin": "创建服务器管理员账户时发生错误。", + "securityKeyManage": "管理安全密钥", + "securityKeyDescription": "添加或删除用于无密码认证的安全密钥", + "securityKeyRegister": "注册新的安全密钥", + "securityKeyList": "您的安全密钥", + "securityKeyNone": "尚未注册安全密钥", + "securityKeyNameRequired": "名称为必填项", + "securityKeyRemove": "删除", + "securityKeyLastUsed": "上次使用:{date}", + "securityKeyNameLabel": "名称", + "securityKeyNamePlaceholder": "为此安全密钥输入名称", + "securityKeyRegisterSuccess": "安全密钥注册成功", + "securityKeyRegisterError": "注册安全密钥失败", + "securityKeyRemoveSuccess": "安全密钥删除成功", + "securityKeyRemoveError": "删除安全密钥失败", + "securityKeyLoadError": "加载安全密钥失败", + "securityKeyLogin": "使用安全密钥登录", + "securityKeyAuthError": "使用安全密钥认证失败", + "securityKeyRecommendation": "考虑在其他设备上注册另一个安全密钥,以确保不会被锁定在您的账户之外。" } diff --git a/package-lock.json b/package-lock.json index 4fd8e822..a67a24e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "0.0.0", "license": "SEE LICENSE IN LICENSE AND README.md", "dependencies": { - "@asteasolutions/zod-to-openapi": "^7.3.2", - "@aws-sdk/client-s3": "3.837.0", + "@asteasolutions/zod-to-openapi": "^7.3.4", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -33,16 +32,18 @@ "@radix-ui/react-tabs": "1.1.12", "@radix-ui/react-toast": "1.2.14", "@radix-ui/react-tooltip": "^1.2.7", - "@react-email/components": "0.0.41", + "@react-email/components": "0.3.1", "@react-email/render": "^1.1.2", - "@react-email/tailwind": "1.0.5", + "@react-email/tailwind": "1.2.1", + "@simplewebauthn/browser": "^13.1.0", + "@simplewebauthn/server": "^9.0.3", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", - "axios": "1.9.0", + "axios": "1.10.0", "better-sqlite3": "11.7.0", "canvas-confetti": "1.9.3", - "class-variance-authority": "0.7.1", + "class-variance-authority": "^0.7.1", "clsx": "2.1.1", "cmdk": "1.1.1", "cookie": "^1.0.2", @@ -50,12 +51,12 @@ "cookies": "^0.9.1", "cors": "2.8.5", "crypto-js": "^4.2.0", - "drizzle-orm": "0.38.3", - "eslint": "9.28.0", - "eslint-config-next": "15.3.3", + "drizzle-orm": "0.44.2", + "eslint": "9.31.0", + "eslint-config-next": "15.3.5", "express": "4.21.2", - "express-rate-limit": "7.5.0", - "glob": "11.0.2", + "express-rate-limit": "7.5.1", + "glob": "11.0.3", "helmet": "8.1.0", "http-errors": "2.0.0", "i": "^0.3.7", @@ -64,70 +65,69 @@ "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", - "lucide-react": "0.511.0", + "lucide-react": "0.525.0", "moment": "2.30.1", - "next": "15.3.3", - "next-intl": "^4.1.0", + "next": "15.3.5", + "next-intl": "^4.3.4", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "6.9.16", - "npm": "^11.4.1", + "nodemailer": "7.0.5", + "npm": "^11.4.2", "oslo": "1.2.1", - "pg": "^8.16.0", + "pg": "^8.16.2", "qrcode.react": "4.2.0", "rate-limit-redis": "^4.2.1", "react": "19.1.0", "react-dom": "19.1.0", "react-easy-sort": "^1.6.0", - "react-hook-form": "7.56.4", + "react-hook-form": "7.60.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", - "semver": "7.7.2", - "stripe": "18.2.1", - "swagger-ui-express": "^5.0.1", - "tailwind-merge": "2.6.0", - "tw-animate-css": "^1.3.3", + "semver": "^7.7.2", + "tailwind-merge": "3.3.1", + "tw-animate-css": "^1.3.5", "uuid": "^11.1.0", "vaul": "1.1.2", "winston": "3.17.0", "winston-daily-rotate-file": "5.0.0", - "ws": "8.18.2", + "ws": "8.18.3", "yargs": "18.0.0", - "zod": "3.25.56", - "zod-validation-error": "3.4.1" + "zod": "3.25.76", + "zod-validation-error": "3.5.2" }, "devDependencies": { - "@dotenvx/dotenvx": "1.44.1", + "@dotenvx/dotenvx": "1.47.3", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/postcss": "^4.1.10", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", "@types/cors": "2.8.19", "@types/crypto-js": "^4.2.2", "@types/express": "5.0.0", + "@types/express-session": "^1.18.2", "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", - "@types/jsonwebtoken": "^9.0.9", - "@types/node": "^22", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24", "@types/nodemailer": "6.4.17", "@types/pg": "8.15.4", - "@types/react": "19.1.7", + "@types/react": "19.1.8", "@types/react-dom": "19.1.6", - "@types/semver": "7.7.0", + "@types/semver": "^7.7.0", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", "@types/yargs": "17.0.33", - "drizzle-kit": "0.31.1", - "esbuild": "0.25.5", + "drizzle-kit": "0.31.4", + "esbuild": "0.25.6", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.0.16", + "react-email": "4.1.0", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", - "tsx": "4.19.4", + "tsx": "4.20.3", "typescript": "^5", - "typescript-eslint": "^8.34.0" + "typescript-eslint": "^8.36.0" } }, "node_modules/@alloc/quick-lru": { @@ -169,876 +169,6 @@ "zod": "^3.20.2" } }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.837.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.837.0.tgz", - "integrity": "sha512-sBjPPG30HIfNwpzWuajCDf7agb4YAxPFFpsp3kwgptJF8PEi0HzQg64bskquMzjqLC2tXsn5rKtDVpQOvs29MQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-node": "3.835.0", - "@aws-sdk/middleware-bucket-endpoint": "3.830.0", - "@aws-sdk/middleware-expect-continue": "3.821.0", - "@aws-sdk/middleware-flexible-checksums": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-location-constraint": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/middleware-ssec": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/signature-v4-multi-region": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/eventstream-serde-browser": "^4.0.4", - "@smithy/eventstream-serde-config-resolver": "^4.1.2", - "@smithy/eventstream-serde-node": "^4.0.4", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-blob-browser": "^4.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/hash-stream-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/md5-js": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.5", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-s3/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.835.0.tgz", - "integrity": "sha512-4J19IcBKU5vL8yw/YWEvbwEGcmCli0rpRyxG53v0K5/3weVPxVBbKfkWcjWVQ4qdxNz2uInfbTde4BRBFxWllQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.835.0.tgz", - "integrity": "sha512-7mnf4xbaLI8rkDa+w6fUU48dG6yDuOgLXEPe4Ut3SbMp1ceJBPMozNHbCwkiyHk3HpxZYf8eVy0wXhJMrxZq5w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.835.0.tgz", - "integrity": "sha512-U9LFWe7+ephNyekpUbzT7o6SmJTmn6xkrPkE0D7pbLojnPVi/8SZKyjtgQGIsAv+2kFkOCqMOIYUKd/0pE7uew==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.835.0.tgz", - "integrity": "sha512-jCdNEsQklil7frDm/BuVKl4ubVoQHRbV6fnkOjmxAJz0/v7cR8JP0jBGlqKKzh3ROh5/vo1/5VUZbCTLpc9dSg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.835.0.tgz", - "integrity": "sha512-nqF6rYRAnJedmvDfrfKygzyeADcduDvtvn7GlbQQbXKeR2l7KnCdhuxHa0FALLvspkHiBx7NtInmvnd5IMuWsw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.835.0.tgz", - "integrity": "sha512-77B8elyZlaEd7vDYyCnYtVLuagIBwuJ0AQ98/36JMGrYX7TT8UVAhiDAfVe0NdUOMORvDNFfzL06VBm7wittYw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.835.0", - "@aws-sdk/credential-provider-http": "3.835.0", - "@aws-sdk/credential-provider-ini": "3.835.0", - "@aws-sdk/credential-provider-process": "3.835.0", - "@aws-sdk/credential-provider-sso": "3.835.0", - "@aws-sdk/credential-provider-web-identity": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.835.0.tgz", - "integrity": "sha512-qXkTt5pAhSi2Mp9GdgceZZFo/cFYrA735efqi/Re/nf0lpqBp8mRM8xv+iAaPHV4Q10q0DlkbEidT1DhxdT/+w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.835.0.tgz", - "integrity": "sha512-jAiEMryaPFXayYGszrc7NcgZA/zrrE3QvvvUBh/Udasg+9Qp5ZELdJCm/p98twNyY9n5i6Ex6VgvdxZ7+iEheQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.835.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/token-providers": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.835.0.tgz", - "integrity": "sha512-zfleEFXDLlcJ7cyfS4xSyCRpd8SVlYZfH3rp0pg2vPYKbnmXVE0r+gPIYXl4L+Yz4A2tizYl63nKCNdtbxadog==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.830.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.830.0.tgz", - "integrity": "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.821.0.tgz", - "integrity": "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.835.0.tgz", - "integrity": "sha512-9ezorQYlr5cQY28zWAReFhNKUTaXsi3TMvXIagMRrSeWtQ7R6TCYnt91xzHRCmFR2kp3zLI+dfoeH+wF3iCKUw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.821.0.tgz", - "integrity": "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.835.0.tgz", - "integrity": "sha512-oPebxpVf9smInHhevHh3APFZagGU+4RPwXEWv9YtYapFvsMq+8QXFvOfxfVZ/mwpe0JVG7EiJzL9/9Kobmts8Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.5.3", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.821.0.tgz", - "integrity": "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.835.0.tgz", - "integrity": "sha512-2gmAYygeE/gzhyF2XlkcbMLYFTbNfV61n+iCFa/ZofJHXYE+RxSyl5g4kujLEs7bVZHmjQZJXhprVSkGccq3/w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@smithy/core": "^3.5.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.835.0.tgz", - "integrity": "sha512-UtmOO0U5QkicjCEv+B32qqRAnS7o2ZkZhC+i3ccH1h3fsfaBshpuuNBwOYAzRCRBeKW5fw3ANFrV/+2FTp4jWg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.835.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.835.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.3", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.12", - "@smithy/middleware-retry": "^4.1.13", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.20", - "@smithy/util-defaults-mode-node": "^4.0.20", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.835.0.tgz", - "integrity": "sha512-rEtJH4dIwJYlXXe5rIH+uTCQmd2VIjuaoHlDY3Dr4nxF6po6U7vKsLfybIU2tgflGVqoqYQnXsfW/kj/Rh+/ow==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.835.0.tgz", - "integrity": "sha512-zN1P3BE+Rv7w7q/CDA8VCQox6SE9QTn0vDtQ47AHA3eXZQQgYzBqgoLgJxR9rKKBIRGZqInJa/VRskLL95VliQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.835.0", - "@aws-sdk/nested-clients": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", - "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", - "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.828.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", - "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", - "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.835.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.835.0.tgz", - "integrity": "sha512-gY63QZ4W5w9JYHYuqvUxiVGpn7IbCt1ODPQB0ZZwGGr3WRmK+yyZxCtFjbYhEQDQLgTWpf8YgVxgQLv2ps0PJg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.835.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", - "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -1186,9 +316,9 @@ } }, "node_modules/@dotenvx/dotenvx": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.44.1.tgz", - "integrity": "sha512-j1QImCqf/XJmhIjC1OPpgiZV9g370HG9MNT9s/CDwCKsoYzNCPEKK+GfsidahJx7yIlBbm+4dPLlGec+bKn7oA==", + "version": "1.47.3", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.47.3.tgz", + "integrity": "sha512-V0jxoEgyTrP6INJYBXxR6qkaS1qUXmrWTz7FZVx706TgXnMnR7LVRi5Bf9z/o0UmZlkavJD13PLediPi4QvUTQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1203,8 +333,7 @@ "which": "^4.0.0" }, "bin": { - "dotenvx": "src/cli/dotenvx.js", - "git-dotenvx": "src/cli/dotenvx.js" + "dotenvx": "src/cli/dotenvx.js" }, "funding": { "url": "https://dotenvx.com" @@ -1232,37 +361,6 @@ "@noble/ciphers": "^1.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -1716,9 +814,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", "cpu": [ "ppc64" ], @@ -1733,9 +831,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", "cpu": [ "arm" ], @@ -1750,9 +848,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", "cpu": [ "arm64" ], @@ -1767,9 +865,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", "cpu": [ "x64" ], @@ -1784,9 +882,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", "cpu": [ "arm64" ], @@ -1801,9 +899,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", "cpu": [ "x64" ], @@ -1818,9 +916,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", "cpu": [ "arm64" ], @@ -1835,9 +933,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", "cpu": [ "x64" ], @@ -1852,9 +950,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", "cpu": [ "arm" ], @@ -1869,9 +967,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", "cpu": [ "arm64" ], @@ -1886,9 +984,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", "cpu": [ "ia32" ], @@ -1903,9 +1001,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", "cpu": [ "loong64" ], @@ -1920,9 +1018,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", "cpu": [ "mips64el" ], @@ -1937,9 +1035,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", "cpu": [ "ppc64" ], @@ -1954,9 +1052,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", "cpu": [ "riscv64" ], @@ -1971,9 +1069,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", "cpu": [ "s390x" ], @@ -1988,9 +1086,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", "cpu": [ "x64" ], @@ -2005,9 +1103,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", "cpu": [ "arm64" ], @@ -2022,9 +1120,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", "cpu": [ "x64" ], @@ -2039,9 +1137,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", "cpu": [ "arm64" ], @@ -2056,9 +1154,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", "cpu": [ "x64" ], @@ -2072,10 +1170,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", "cpu": [ "x64" ], @@ -2090,9 +1205,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", "cpu": [ "arm64" ], @@ -2107,9 +1222,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", "cpu": [ "ia32" ], @@ -2124,9 +1239,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", "cpu": [ "x64" ], @@ -2180,9 +1295,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", - "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -2194,18 +1309,18 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", - "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -2238,9 +1353,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", - "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2381,6 +1496,12 @@ "tslib": "2" } }, + "node_modules/@hexagon/base64": { + "version": "1.1.28", + "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz", + "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", + "license": "MIT" + }, "node_modules/@hookform/resolvers": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", @@ -2451,146 +1572,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", @@ -2607,22 +1588,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", @@ -2639,72 +1604,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" - } - }, "node_modules/@img/sharp-linux-x64": { "version": "0.34.2", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", @@ -2727,28 +1626,6 @@ "@img/sharp-libvips-linux-x64": "1.1.0" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" - } - }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.34.2", "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", @@ -2771,82 +1648,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.1.0" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -2957,37 +1758,31 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { + "node_modules/@levischuck/tiny-cbor": { "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", - "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } + "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", + "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==", + "license": "MIT" }, "node_modules/@next/env": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", - "integrity": "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.5.tgz", + "integrity": "sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.3.tgz", - "integrity": "sha512-VKZJEiEdpKkfBmcokGjHu0vGDG+8CehGs90tBEy/IDoDDKGngeyIStt2MmE5FYNyU9BhgR7tybNWTAJY/30u+Q==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.5.tgz", + "integrity": "sha512-BZwWPGfp9po/rAnJcwUBaM+yT/+yTWIkWdyDwc74G9jcfTrNrmsHe+hXHljV066YNdVs8cxROxX5IgMQGX190w==", "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz", - "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.5.tgz", + "integrity": "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==", "cpu": [ "arm64" ], @@ -3001,9 +1796,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz", - "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.5.tgz", + "integrity": "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==", "cpu": [ "x64" ], @@ -3017,9 +1812,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz", - "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.5.tgz", + "integrity": "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==", "cpu": [ "arm64" ], @@ -3033,9 +1828,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz", - "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.5.tgz", + "integrity": "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==", "cpu": [ "arm64" ], @@ -3049,9 +1844,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz", - "integrity": "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.5.tgz", + "integrity": "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==", "cpu": [ "x64" ], @@ -3065,9 +1860,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.3.tgz", - "integrity": "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.5.tgz", + "integrity": "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==", "cpu": [ "x64" ], @@ -3081,9 +1876,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz", - "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.5.tgz", + "integrity": "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==", "cpu": [ "arm64" ], @@ -3097,9 +1892,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz", - "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.5.tgz", + "integrity": "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==", "cpu": [ "x64" ], @@ -3179,134 +1974,6 @@ "@node-rs/argon2-win32-x64-msvc": "2.0.2" } }, - "node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-2.0.2.tgz", - "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-android-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-2.0.2.tgz", - "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-2.0.2.tgz", - "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-2.0.2.tgz", - "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-freebsd-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-2.0.2.tgz", - "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-2.0.2.tgz", - "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-2.0.2.tgz", - "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-2.0.2.tgz", - "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", @@ -3339,70 +2006,6 @@ "node": ">= 10" } }, - "node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-2.0.2.tgz", - "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-2.0.2.tgz", - "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-2.0.2.tgz", - "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-2.0.2.tgz", - "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/bcrypt": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", @@ -3432,134 +2035,6 @@ "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, - "node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", - "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/bcrypt-linux-x64-gnu": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", @@ -3592,103 +2067,6 @@ "node": ">= 10" } }, - "node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", - "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3779,6 +2157,64 @@ "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==", "license": "MIT" }, + "node_modules/@peculiar/asn1-android": { + "version": "2.3.16", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.3.16.tgz", + "integrity": "sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.15.tgz", + "integrity": "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.3.15.tgz", + "integrity": "sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "@peculiar/asn1-x509": "^2.3.15", + "asn1js": "^3.0.5", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", + "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", + "license": "MIT", + "dependencies": { + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.15.tgz", + "integrity": "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.15", + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -4856,9 +3292,9 @@ } }, "node_modules/@react-email/button": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.0.19.tgz", - "integrity": "sha512-HYHrhyVGt7rdM/ls6FuuD6XE7fa7bjZTJqB2byn6/oGsfiEZaogY77OtoLL/mrQHjHjZiJadtAMSik9XLcm7+A==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.0.tgz", + "integrity": "sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4868,9 +3304,9 @@ } }, "node_modules/@react-email/code-block": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.0.13.tgz", - "integrity": "sha512-4DE4yPSgKEOnZMzcrDvRuD6mxsNxOex0hCYEG9F9q23geYgb2WCCeGBvIUXVzK69l703Dg4Vzrd5qUjl+JfcwA==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", + "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" @@ -4907,14 +3343,14 @@ } }, "node_modules/@react-email/components": { - "version": "0.0.41", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.0.41.tgz", - "integrity": "sha512-WUI3wHwra3QS0pwrovSU6b0I0f3TvY33ph0y44LuhSYDSQlMRyeOzgoT6HRDY5FXMDF57cHYq9WoKwpwP0yd7Q==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.1.tgz", + "integrity": "sha512-FqcyGaUpJJu8zfYGSS+qaSy7Zc2BFAswBc/LvHeSV4iTQMZMD8Dy7aS/NvP1SQMg5vjsO1aMpGFdrD4NBY58dw==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", - "@react-email/button": "0.0.19", - "@react-email/code-block": "0.0.13", + "@react-email/button": "0.2.0", + "@react-email/code-block": "0.1.0", "@react-email/code-inline": "0.0.5", "@react-email/column": "0.0.13", "@react-email/container": "0.0.15", @@ -4927,11 +3363,11 @@ "@react-email/link": "0.0.12", "@react-email/markdown": "0.0.15", "@react-email/preview": "0.0.13", - "@react-email/render": "1.1.2", + "@react-email/render": "1.1.3", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.0.5", - "@react-email/text": "0.1.4" + "@react-email/tailwind": "1.2.1", + "@react-email/text": "0.1.5" }, "engines": { "node": ">=18.0.0" @@ -4940,24 +3376,6 @@ "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, - "node_modules/@react-email/components/node_modules/@react-email/render": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.2.tgz", - "integrity": "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==", - "license": "MIT", - "dependencies": { - "html-to-text": "^9.0.5", - "prettier": "^3.5.3", - "react-promise-suspense": "^0.3.4" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, "node_modules/@react-email/container": { "version": "0.0.15", "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", @@ -5121,9 +3539,9 @@ } }, "node_modules/@react-email/tailwind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.0.5.tgz", - "integrity": "sha512-BH00cZSeFfP9HiDASl+sPHi7Hh77W5nzDgdnxtsVr/m3uQD9g180UwxcE3PhOfx0vRdLzQUU8PtmvvDfbztKQg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.1.tgz", + "integrity": "sha512-SmVyDuNQLJwO3wHEe/snSTaRhf/Exldy5DQU/RyPjcSPC0EuXXYwFlBr16br8jJSxkZA/fL91AxKL7HbbWp0Rw==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -5133,9 +3551,9 @@ } }, "node_modules/@react-email/text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.4.tgz", - "integrity": "sha512-cMNE02y8172DocpNGh97uV5HSTawaS4CKG/zOku8Pu+m6ehBKbAjgtQZDIxhgstw8+TWraFB8ltS1DPjfG8nLA==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", + "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -5156,13 +3574,6 @@ "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", "license": "MIT" }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" - }, "node_modules/@schummar/icu-type-parser": { "version": "1.21.5", "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", @@ -5182,737 +3593,37 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@simplewebauthn/browser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.1.2.tgz", + "integrity": "sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw==", + "license": "MIT" }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", - "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", - "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", - "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.6.0.tgz", - "integrity": "sha512-Pgvfb+TQ4wUNLyHzvgCP4aYZMh16y7GcfF59oirRHcgGgkH1e/s9C0nv/v3WP+Quymyr5je71HeFQCwh+44XLg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.0.8", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", - "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.4.tgz", - "integrity": "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.3.1", - "@smithy/util-hex-encoding": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.4.tgz", - "integrity": "sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.2.tgz", - "integrity": "sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.4.tgz", - "integrity": "sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.4.tgz", - "integrity": "sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", - "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.4.tgz", - "integrity": "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.0.0", - "@smithy/chunked-blob-reader-native": "^4.0.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", - "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.4.tgz", - "integrity": "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", - "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.4.tgz", - "integrity": "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", - "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.13.tgz", - "integrity": "sha512-xg3EHV/Q5ZdAO5b0UiIMj3RIOCobuS40pBBODguUDVdko6YK6QIzCVRrHTogVuEKglBWqWenRnZ71iZnLL3ZAQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.6.0", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.14.tgz", - "integrity": "sha512-eoXaLlDGpKvdmvt+YBfRXE7HmIEtFF+DJCbTPwuLunP0YUnrydl+C4tS+vEM0+nyxXrX3PSUFqC+lP1+EHB1Tw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/service-error-classification": "^4.0.6", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/@simplewebauthn/server": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-9.0.3.tgz", + "integrity": "sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==", "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", - "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", - "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" + "@hexagon/base64": "^1.1.27", + "@levischuck/tiny-cbor": "^0.2.2", + "@peculiar/asn1-android": "^2.3.10", + "@peculiar/asn1-ecc": "^2.3.8", + "@peculiar/asn1-rsa": "^2.3.8", + "@peculiar/asn1-schema": "^2.3.8", + "@peculiar/asn1-x509": "^2.3.8", + "@simplewebauthn/types": "^9.0.1", + "cross-fetch": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@smithy/middleware-stack": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", - "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", - "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", - "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", - "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", - "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", - "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", - "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", - "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", - "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.5.tgz", - "integrity": "sha512-+lynZjGuUFJaMdDYSTMnP/uPBBXXukVfrJlP+1U/Dp5SFTEI++w6NMga8DjOENxecOF71V9Z2DllaVDYRnGlkg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.6.0", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", - "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.21.tgz", - "integrity": "sha512-wM0jhTytgXu3wzJoIqpbBAG5U6BwiubZ6QKzSbP7/VbmF1v96xlAbX2Am/mz0Zep0NLvLh84JT0tuZnk3wmYQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.21.tgz", - "integrity": "sha512-/F34zkoU0GzpUgLJydHY8Rxu9lBn8xQC/s/0M0U9lLBkYbA1htaAFjWYJzpzsbXPuri5D1H8gjp2jBum05qBrA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.1.4", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", - "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", - "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", - "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.0.6", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", - "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.6.tgz", - "integrity": "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@simplewebauthn/types": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-9.0.1.tgz", + "integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==", + "license": "MIT" }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", @@ -5993,125 +3704,6 @@ "@tailwindcss/oxide-win32-x64-msvc": "4.1.10" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.10.tgz", - "integrity": "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.10.tgz", - "integrity": "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.10.tgz", - "integrity": "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.10.tgz", - "integrity": "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.10.tgz", - "integrity": "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.10.tgz", - "integrity": "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.10.tgz", - "integrity": "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.10.tgz", @@ -6146,70 +3738,6 @@ "node": ">= 10" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.10.tgz", - "integrity": "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.10", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.10.tgz", - "integrity": "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz", - "integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/postcss": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.10.tgz", @@ -6257,16 +3785,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/better-sqlite3": { "version": "7.6.12", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", @@ -6357,6 +3875,16 @@ "@types/send": "*" } }, + "node_modules/@types/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -6416,13 +3944,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.33.tgz", - "integrity": "sha512-wzoocdnnpSxZ+6CjW4ADCK1jVmd1S/J3ArNWfn8FDDQtRm8dkDg7TA+mvek2wNrfCgwuZxqEOiB9B1XCJ6+dbw==", + "version": "24.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", + "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.8.0" } }, "node_modules/@types/nodemailer": { @@ -6462,9 +3990,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.7.tgz", - "integrity": "sha512-BnsPLV43ddr05N71gaGzyZ5hzkCmGwhMvYc8zmvI8Ci1bRkkDSzDDVfAXfN2tk748OwI7ediiPX6PfT9p0QGVg==", + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6528,12 +4056,6 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -6562,16 +4084,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz", - "integrity": "sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/type-utils": "8.35.0", - "@typescript-eslint/utils": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -6585,7 +4107,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.35.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -6600,15 +4122,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.0.tgz", - "integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -6624,13 +4146,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.0.tgz", - "integrity": "sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.35.0", - "@typescript-eslint/types": "^8.35.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -6645,13 +4167,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.0.tgz", - "integrity": "sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6662,9 +4184,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.0.tgz", - "integrity": "sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6678,13 +4200,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.0.tgz", - "integrity": "sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/utils": "8.35.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6701,9 +4224,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.0.tgz", - "integrity": "sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6714,15 +4237,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.0.tgz", - "integrity": "sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.35.0", - "@typescript-eslint/tsconfig-utils": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6794,15 +4317,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.0.tgz", - "integrity": "sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6817,12 +4340,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.0.tgz", - "integrity": "sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -6833,175 +4356,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz", - "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz", - "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.2.tgz", - "integrity": "sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz", - "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz", - "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz", - "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz", - "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz", - "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz", - "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz", - "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz", - "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz", - "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz", - "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz", @@ -7028,61 +4382,6 @@ "linux" ] }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz", - "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz", - "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz", - "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz", - "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -7405,6 +4704,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.6.tgz", + "integrity": "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==", + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -7457,9 +4770,9 @@ } }, "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -7595,12 +4908,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -7808,6 +5115,16 @@ "node": ">=18" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -8069,6 +5386,23 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -8153,6 +5487,35 @@ "node": ">= 0.10" } }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -8298,9 +5661,9 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "license": "MIT" }, "node_modules/decompress-response": { @@ -8522,15 +5885,15 @@ } }, "node_modules/drizzle-kit": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.1.tgz", - "integrity": "sha512-PUjYKWtzOzPtdtQlTHQG3qfv4Y0XT8+Eas6UbxCmxTj7qgMf+39dDujf1BP1I+qqZtw9uzwTh8jYtkMuCq+B0Q==", + "version": "0.31.4", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz", + "integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==", "dev": true, "license": "MIT", "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", - "esbuild": "^0.25.2", + "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { @@ -8538,9 +5901,9 @@ } }, "node_modules/drizzle-orm": { - "version": "0.38.3", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.38.3.tgz", - "integrity": "sha512-w41Y+PquMpSff/QDRGdItG0/aWca+/J3Sda9PPGkTxBtjWQvgU1jxlFBXdjog5tYvTu58uvi3PwR1NuCx0KeZg==", + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.2.tgz", + "integrity": "sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", @@ -8551,24 +5914,24 @@ "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", - "@planetscale/database": ">=1", + "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", - "@types/react": ">=18", "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", + "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", - "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, @@ -8612,10 +5975,10 @@ "@types/pg": { "optional": true }, - "@types/react": { + "@types/sql.js": { "optional": true }, - "@types/sql.js": { + "@upstash/redis": { "optional": true }, "@vercel/postgres": { @@ -8633,6 +5996,9 @@ "expo-sqlite": { "optional": true }, + "gel": { + "optional": true + }, "knex": { "optional": true }, @@ -8651,9 +6017,6 @@ "prisma": { "optional": true }, - "react": { - "optional": true - }, "sql.js": { "optional": true }, @@ -9022,9 +6385,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9035,31 +6398,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" } }, "node_modules/esbuild-node-externals": { @@ -9119,18 +6483,18 @@ } }, "node_modules/eslint": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", - "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.28.0", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -9142,9 +6506,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -9179,12 +6543,12 @@ } }, "node_modules/eslint-config-next": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.3.tgz", - "integrity": "sha512-QJLv/Ouk2vZnxL4b67njJwTLjTf7uZRltI0LL4GERYR4qMF5z08+gxkfODAeaK7TiC6o+cER91bDaEnwrTWV6Q==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.5.tgz", + "integrity": "sha512-oQdvnIgP68wh2RlR3MdQpvaJ94R6qEFl+lnu8ZKxPj5fsAHrSF/HlAOZcsimLw3DT6bnEQIUdbZC2Ab6sWyptg==", "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.3.3", + "@next/eslint-plugin-next": "15.3.5", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -9611,9 +6975,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "license": "MIT", "engines": { "node": ">= 16" @@ -9622,7 +6986,7 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "express": ">= 4.11" } }, "node_modules/express/node_modules/cookie": { @@ -9649,6 +7013,13 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9695,28 +7066,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -9997,13 +7346,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense", - "optional": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -10173,14 +7515,14 @@ "license": "MIT" }, "node_modules/glob": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", - "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -11256,6 +8598,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -11331,132 +8683,6 @@ "lightningcss-win32-x64-msvc": "1.30.1" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", @@ -11499,48 +8725,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11672,9 +8856,9 @@ } }, "node_modules/lucide-react": { - "version": "0.511.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz", - "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -11732,29 +8916,6 @@ "node": ">= 0.6" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memfs-browser": { - "version": "3.5.10302", - "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", - "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "memfs": "3.5.3" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -12040,12 +9201,12 @@ } }, "node_modules/next": { - "version": "15.3.3", - "resolved": "https://registry.npmjs.org/next/-/next-15.3.3.tgz", - "integrity": "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==", + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.5.tgz", + "integrity": "sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw==", "license": "MIT", "dependencies": { - "@next/env": "15.3.3", + "@next/env": "15.3.5", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -12060,14 +9221,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.3.3", - "@next/swc-darwin-x64": "15.3.3", - "@next/swc-linux-arm64-gnu": "15.3.3", - "@next/swc-linux-arm64-musl": "15.3.3", - "@next/swc-linux-x64-gnu": "15.3.3", - "@next/swc-linux-x64-musl": "15.3.3", - "@next/swc-win32-arm64-msvc": "15.3.3", - "@next/swc-win32-x64-msvc": "15.3.3", + "@next/swc-darwin-arm64": "15.3.5", + "@next/swc-darwin-x64": "15.3.5", + "@next/swc-linux-arm64-gnu": "15.3.5", + "@next/swc-linux-arm64-musl": "15.3.5", + "@next/swc-linux-x64-gnu": "15.3.5", + "@next/swc-linux-x64-musl": "15.3.5", + "@next/swc-win32-arm64-msvc": "15.3.5", + "@next/swc-win32-x64-msvc": "15.3.5", "sharp": "^0.34.1" }, "peerDependencies": { @@ -12094,9 +9255,9 @@ } }, "node_modules/next-intl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.1.tgz", - "integrity": "sha512-FylHpOoQw5MpOyJt4cw8pNEGba7r3jKDSqt112fmBqXVceGR5YncmqpxS5MvSHsWRwbjqpOV8OsZCIY/4f4HWg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.3.4.tgz", + "integrity": "sha512-VWLIDlGbnL/o4LnveJTJD1NOYN8lh3ZAGTWw2krhfgg53as3VsS4jzUVnArJdqvwtlpU/2BIDbWTZ7V4o1jFEw==", "funding": [ { "type": "individual", @@ -12107,7 +9268,7 @@ "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", - "use-intl": "^4.3.1" + "use-intl": "^4.3.4" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", @@ -12230,9 +9391,9 @@ } }, "node_modules/nodemailer": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", - "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", + "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -14725,6 +11886,26 @@ "inBundle": true, "license": "ISC" }, + "node_modules/nypm": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", + "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^2.0.0", + "tinyexec": "^0.3.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -15043,26 +12224,6 @@ "@node-rs/bcrypt": "1.9.0" } }, - "node_modules/oslo/node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/oslo/node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", @@ -15088,134 +12249,6 @@ "@node-rs/argon2-win32-x64-msvc": "1.7.0" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", - "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", - "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", - "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", @@ -15248,83 +12281,6 @@ "node": ">= 10" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@tybys/wasm-util": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", - "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -15468,6 +12424,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/peberminta": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", @@ -15584,6 +12547,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/plimit-lit": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", @@ -15733,6 +12708,20 @@ "node": ">=6" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -15782,6 +12771,24 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/qrcode.react": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", @@ -15941,9 +12948,9 @@ "license": "0BSD" }, "node_modules/react-email": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.0.16.tgz", - "integrity": "sha512-auhFU+nQxAkKkP6lQhPyGsa9exwfUEzp2BwZnjHokCwphZlg30tu4t1LgdKRwGPYsi7XNGy6asbVLAUhOVpzzg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", + "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", "dev": true, "license": "MIT", "dependencies": { @@ -15955,15 +12962,18 @@ "debounce": "^2.0.0", "esbuild": "^0.25.0", "glob": "^11.0.0", + "jiti": "2.4.2", "log-symbols": "^7.0.0", "mime-types": "^3.0.0", - "next": "^15.3.1", "normalize-path": "^3.0.0", + "nypm": "0.6.0", "ora": "^8.0.0", - "socket.io": "^4.8.1" + "prompts": "2.4.2", + "socket.io": "^4.8.1", + "tsconfig-paths": "4.2.0" }, "bin": { - "email": "dist/cli/index.mjs" + "email": "dist/index.js" }, "engines": { "node": ">=18.0.0" @@ -15992,6 +13002,19 @@ "node": ">=18" } }, + "node_modules/react-email/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/react-email/node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -16015,10 +13038,25 @@ "node": ">= 0.6" } }, + "node_modules/react-email/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/react-hook-form": { - "version": "7.56.4", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", - "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", + "version": "7.60.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.60.0.tgz", + "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -16780,6 +13818,13 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -17256,38 +14301,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stripe": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.2.1.tgz", - "integrity": "sha512-GwB1B7WSwEBzW4dilgyJruUYhbGMscrwuyHsPUmSRKrGHZ5poSh2oU9XKdii5BFVJzXHn35geRvGJ6R8bYcp8w==", - "license": "MIT", - "dependencies": { - "qs": "^6.11.0" - }, - "engines": { - "node": ">=12.*" - }, - "peerDependencies": { - "@types/node": ">=12.x.x" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -17335,34 +14348,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swagger-ui-dist": { - "version": "5.25.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.25.2.tgz", - "integrity": "sha512-V4JyoygUe5nCbn7bAD0fVKSC0yNcL3ROIQtGC7M0NATKuyosCSmMU6T0yDZIIuGpSxjsjZh/D2Ejb8lnF2jjxw==", - "license": "Apache-2.0", - "dependencies": { - "@scarf/scarf": "=1.4.0" - } - }, - "node_modules/swagger-ui-express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", - "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "license": "MIT", - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, "node_modules/tailwind-merge": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", - "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", "license": "MIT", "funding": { "type": "github", @@ -17443,6 +14432,13 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -17480,6 +14476,12 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -17625,9 +14627,9 @@ } }, "node_modules/tsx": { - "version": "4.19.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", - "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17657,9 +14659,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.4.tgz", - "integrity": "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.5.tgz", + "integrity": "sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -17778,15 +14780,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.0.tgz", - "integrity": "sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.35.0", - "@typescript-eslint/parser": "8.35.0", - "@typescript-eslint/utils": "8.35.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -17819,9 +14822,9 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", "devOptional": true, "license": "MIT" }, @@ -17899,9 +14902,9 @@ } }, "node_modules/use-intl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.1.tgz", - "integrity": "sha512-8Xn5RXzeHZhWqqZimi1wi2pKFqm0NxRUOB41k1QdjbPX+ysoeLW3Ey+fi603D/e5EGb0fYw8WzjgtUagJdlIvg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.3.4.tgz", + "integrity": "sha512-sHfiU0QeJ1rirNWRxvCyvlSh9+NczcOzRnPyMeo2rtHXhVnBsvMRjE+UG4eh3lRhCxrvcqei/I0lBxsc59on1w==", "license": "MIT", "dependencies": { "@formatjs/fast-memoize": "^2.2.0", @@ -18002,6 +15005,22 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -18270,9 +15289,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -18405,24 +15424,24 @@ } }, "node_modules/zod": { - "version": "3.25.56", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.56.tgz", - "integrity": "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-validation-error": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.1.tgz", - "integrity": "sha512-1KP64yqDPQ3rupxNv7oXhf7KdhHHgaqbKuspVoiN93TT0xrBjql+Svjkdjq/Qh/7GSMmgQs3AfvBT0heE35thw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz", + "integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "zod": "^3.24.4" + "zod": "^3.25.0" } } } diff --git a/package.json b/package.json index 76e26ec3..b402add8 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,7 @@ "build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs" }, "dependencies": { - "@asteasolutions/zod-to-openapi": "^7.3.2", - "@aws-sdk/client-s3": "3.837.0", + "@asteasolutions/zod-to-openapi": "^7.3.4", "@hookform/resolvers": "3.9.1", "@node-rs/argon2": "^2.0.2", "@oslojs/crypto": "1.0.1", @@ -51,16 +50,18 @@ "@radix-ui/react-tabs": "1.1.12", "@radix-ui/react-toast": "1.2.14", "@radix-ui/react-tooltip": "^1.2.7", - "@react-email/components": "0.0.41", + "@react-email/components": "0.3.1", "@react-email/render": "^1.1.2", - "@react-email/tailwind": "1.0.5", + "@simplewebauthn/browser": "^13.1.0", + "@simplewebauthn/server": "^9.0.3", + "@react-email/tailwind": "1.2.1", "@tailwindcss/forms": "^0.5.10", "@tanstack/react-table": "8.21.3", "arctic": "^3.7.0", - "axios": "1.9.0", + "axios": "1.10.0", "better-sqlite3": "11.7.0", "canvas-confetti": "1.9.3", - "class-variance-authority": "0.7.1", + "class-variance-authority": "^0.7.1", "clsx": "2.1.1", "cmdk": "1.1.1", "cookie": "^1.0.2", @@ -68,12 +69,12 @@ "cookies": "^0.9.1", "cors": "2.8.5", "crypto-js": "^4.2.0", - "drizzle-orm": "0.38.3", - "eslint": "9.28.0", - "eslint-config-next": "15.3.3", + "drizzle-orm": "0.44.2", + "eslint": "9.31.0", + "eslint-config-next": "15.3.5", "express": "4.21.2", - "express-rate-limit": "7.5.0", - "glob": "11.0.2", + "express-rate-limit": "7.5.1", + "glob": "11.0.3", "helmet": "8.1.0", "http-errors": "2.0.0", "i": "^0.3.7", @@ -82,70 +83,69 @@ "jmespath": "^0.16.0", "js-yaml": "4.1.0", "jsonwebtoken": "^9.0.2", - "lucide-react": "0.511.0", + "lucide-react": "0.525.0", "moment": "2.30.1", - "next": "15.3.3", - "next-intl": "^4.1.0", + "next": "15.3.5", + "next-intl": "^4.3.4", "next-themes": "0.4.6", "node-cache": "5.1.2", "node-fetch": "3.3.2", - "nodemailer": "6.9.16", - "npm": "^11.4.1", + "nodemailer": "7.0.5", + "npm": "^11.4.2", "oslo": "1.2.1", - "pg": "^8.16.0", + "pg": "^8.16.2", "qrcode.react": "4.2.0", "rate-limit-redis": "^4.2.1", "react": "19.1.0", "react-dom": "19.1.0", "react-easy-sort": "^1.6.0", - "react-hook-form": "7.56.4", + "react-hook-form": "7.60.0", "react-icons": "^5.5.0", "rebuild": "0.1.2", - "semver": "7.7.2", - "stripe": "18.2.1", - "swagger-ui-express": "^5.0.1", - "tailwind-merge": "2.6.0", - "tw-animate-css": "^1.3.3", + "semver": "^7.7.2", + "tailwind-merge": "3.3.1", + "tw-animate-css": "^1.3.5", "uuid": "^11.1.0", "vaul": "1.1.2", "winston": "3.17.0", "winston-daily-rotate-file": "5.0.0", - "ws": "8.18.2", - "yargs": "18.0.0", - "zod": "3.25.56", - "zod-validation-error": "3.4.1" + "ws": "8.18.3", + "zod": "3.25.76", + "zod-validation-error": "3.5.2", + "yargs": "18.0.0" }, "devDependencies": { - "@dotenvx/dotenvx": "1.44.1", + "@dotenvx/dotenvx": "1.47.3", "@esbuild-plugins/tsconfig-paths": "0.1.2", - "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/postcss": "^4.1.10", "@types/better-sqlite3": "7.6.12", "@types/cookie-parser": "1.4.9", "@types/cors": "2.8.19", "@types/crypto-js": "^4.2.2", "@types/express": "5.0.0", + "@types/express-session": "^1.18.2", "@types/jmespath": "^0.15.2", "@types/js-yaml": "4.0.9", - "@types/jsonwebtoken": "^9.0.9", - "@types/node": "^22", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24", "@types/nodemailer": "6.4.17", "@types/pg": "8.15.4", - "@types/react": "19.1.7", + "@types/react": "19.1.8", "@types/react-dom": "19.1.6", - "@types/semver": "7.7.0", + "@types/semver": "^7.7.0", "@types/swagger-ui-express": "^4.1.8", "@types/ws": "8.18.1", "@types/yargs": "17.0.33", - "drizzle-kit": "0.31.1", - "esbuild": "0.25.5", + "drizzle-kit": "0.31.4", + "esbuild": "0.25.6", "esbuild-node-externals": "1.18.0", "postcss": "^8", - "react-email": "4.0.16", + "react-email": "4.1.0", "tailwindcss": "^4.1.4", "tsc-alias": "1.8.16", - "tsx": "4.19.4", + "tsx": "4.20.3", "typescript": "^5", - "typescript-eslint": "^8.34.0" + "typescript-eslint": "^8.36.0" }, "overrides": { "emblor": { diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 95c3bdcb..ee2c5dac 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -56,6 +56,8 @@ export enum ActionsEnum { // removeUserAction = "removeUserAction", // removeUserSite = "removeUserSite", getOrgUser = "getOrgUser", + updateUser = "updateUser", + getUser = "getUser", setResourcePassword = "setResourcePassword", setResourcePincode = "setResourcePincode", setResourceWhitelist = "setResourceWhitelist", diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 962aac70..1a2175c4 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -132,6 +132,7 @@ export const users = pgTable("user", { }), passwordHash: varchar("passwordHash"), twoFactorEnabled: boolean("twoFactorEnabled").notNull().default(false), + twoFactorSetupRequested: boolean("twoFactorSetupRequested").default(false), twoFactorSecret: varchar("twoFactorSecret"), emailVerified: boolean("emailVerified").notNull().default(false), dateCreated: varchar("dateCreated").notNull(), @@ -560,6 +561,30 @@ export const roleClients = pgTable("roleClients", { .references(() => clients.clientId, { onDelete: "cascade" }) }); +export const securityKeys = pgTable("webauthnCredentials", { + credentialId: varchar("credentialId").primaryKey(), + userId: varchar("userId").notNull().references(() => users.userId, { + onDelete: "cascade" + }), + publicKey: varchar("publicKey").notNull(), + signCount: integer("signCount").notNull(), + transports: varchar("transports"), + name: varchar("name"), + lastUsed: varchar("lastUsed").notNull(), + dateCreated: varchar("dateCreated").notNull(), + securityKeyName: varchar("securityKeyName") +}); + +export const webauthnChallenge = pgTable("webauthnChallenge", { + sessionId: varchar("sessionId").primaryKey(), + challenge: varchar("challenge").notNull(), + securityKeyName: varchar("securityKeyName"), + userId: varchar("userId").references(() => users.userId, { + onDelete: "cascade" + }), + expiresAt: bigint("expiresAt", { mode: "number" }).notNull() // Unix timestamp +}); + export type Org = InferSelectModel; export type User = InferSelectModel; export type Site = InferSelectModel; diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 20347cf7..c4191c34 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -147,6 +147,9 @@ export const users = sqliteTable("user", { twoFactorEnabled: integer("twoFactorEnabled", { mode: "boolean" }) .notNull() .default(false), + twoFactorSetupRequested: integer("twoFactorSetupRequested", { + mode: "boolean" + }).default(false), twoFactorSecret: text("twoFactorSecret"), emailVerified: integer("emailVerified", { mode: "boolean" }) .notNull() @@ -157,6 +160,29 @@ export const users = sqliteTable("user", { .default(false) }); +export const securityKeys = sqliteTable("webauthnCredentials", { + credentialId: text("credentialId").primaryKey(), + userId: text("userId").notNull().references(() => users.userId, { + onDelete: "cascade" + }), + publicKey: text("publicKey").notNull(), + signCount: integer("signCount").notNull(), + transports: text("transports"), + name: text("name"), + lastUsed: text("lastUsed").notNull(), + dateCreated: text("dateCreated").notNull() +}); + +export const webauthnChallenge = sqliteTable("webauthnChallenge", { + sessionId: text("sessionId").primaryKey(), + challenge: text("challenge").notNull(), + securityKeyName: text("securityKeyName"), + userId: text("userId").references(() => users.userId, { + onDelete: "cascade" + }), + expiresAt: integer("expiresAt").notNull() // Unix timestamp +}); + export const newts = sqliteTable("newt", { newtId: text("id").primaryKey(), secretHash: text("secretHash").notNull(), diff --git a/server/index.ts b/server/index.ts index c8405342..55b34543 100644 --- a/server/index.ts +++ b/server/index.ts @@ -36,7 +36,7 @@ declare global { interface Request { apiKey?: ApiKey; user?: User; - session?: Session; + session: Session; userOrg?: UserOrg; apiKeyOrg?: ApiKeyOrg; userOrgRoleId?: number; diff --git a/server/lib/totp.ts b/server/lib/totp.ts new file mode 100644 index 00000000..d9f819ab --- /dev/null +++ b/server/lib/totp.ts @@ -0,0 +1,10 @@ +import { alphabet, generateRandomString } from "oslo/crypto"; + +export async function generateBackupCodes(): Promise { + const codes = []; + for (let i = 0; i < 10; i++) { + const code = generateRandomString(6, alphabet("0-9", "A-Z", "a-z")); + codes.push(code); + } + return codes; +} diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 0a8f6339..cc8fd630 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -6,9 +6,10 @@ export * from "./requestTotpSecret"; export * from "./disable2fa"; export * from "./verifyEmail"; export * from "./requestEmailVerificationCode"; -export * from "./changePassword"; -export * from "./requestPasswordReset"; export * from "./resetPassword"; -export * from "./checkResourceSession"; +export * from "./requestPasswordReset"; export * from "./setServerAdmin"; -export * from "./initialSetupComplete"; \ No newline at end of file +export * from "./initialSetupComplete"; +export * from "./changePassword"; +export * from "./checkResourceSession"; +export * from "./securityKey"; diff --git a/server/routers/auth/login.ts b/server/routers/auth/login.ts index f5f7ff77..cd51e46a 100644 --- a/server/routers/auth/login.ts +++ b/server/routers/auth/login.ts @@ -4,7 +4,7 @@ import { serializeSessionCookie } from "@server/auth/sessions/app"; import { db } from "@server/db"; -import { users } from "@server/db"; +import { users, securityKeys } from "@server/db"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq, and } from "drizzle-orm"; @@ -21,10 +21,7 @@ import { UserType } from "@server/types/UserTypes"; export const loginBodySchema = z .object({ - email: z - .string() - .toLowerCase() - .email(), + email: z.string().toLowerCase().email(), password: z.string(), code: z.string().optional() }) @@ -35,10 +32,10 @@ export type LoginBody = z.infer; export type LoginResponse = { codeRequested?: boolean; emailVerificationRequired?: boolean; + useSecurityKey?: boolean; + twoFactorSetupRequired?: boolean; }; -export const dynamic = "force-dynamic"; - export async function login( req: Request, res: Response, @@ -109,6 +106,35 @@ export async function login( ); } + // Check if user has security keys registered + const userSecurityKeys = await db + .select() + .from(securityKeys) + .where(eq(securityKeys.userId, existingUser.userId)); + + if (userSecurityKeys.length > 0) { + return response(res, { + data: { useSecurityKey: true }, + success: true, + error: false, + message: "Security key authentication required", + status: HttpCode.OK + }); + } + + if ( + existingUser.twoFactorSetupRequested && + !existingUser.twoFactorEnabled + ) { + return response(res, { + data: { twoFactorSetupRequired: true }, + success: true, + error: false, + message: "Two-factor authentication setup required", + status: HttpCode.ACCEPTED + }); + } + if (existingUser.twoFactorEnabled) { if (!code) { return response<{ codeRequested: boolean }>(res, { diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index 6dc2878b..753867b6 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -7,17 +7,19 @@ import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; import { User, users } from "@server/db"; -import { eq } from "drizzle-orm"; +import { eq, and } from "drizzle-orm"; import { createTOTPKeyURI } from "oslo/otp"; import logger from "@server/logger"; import { verifyPassword } from "@server/auth/password"; import { unauthorized } from "@server/auth/unauthorizedResponse"; -import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; +import { verifySession } from "@server/auth/sessions/verifySession"; +import config from "@server/lib/config"; export const requestTotpSecretBody = z .object({ - password: z.string() + password: z.string(), + email: z.string().email().optional() }) .strict(); @@ -44,9 +46,42 @@ export async function requestTotpSecret( ); } - const { password } = parsedBody.data; + const { password, email } = parsedBody.data; - const user = req.user as User; + const { user: sessionUser, session: existingSession } = await verifySession(req); + + let user: User | null = sessionUser; + if (!existingSession) { + if (!email) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Email is required for two-factor authentication setup" + ) + ); + } + const [res] = await db + .select() + .from(users) + .where( + and(eq(users.type, UserType.Internal), eq(users.email, email)) + ); + user = res; + } + + if (!user) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Username or password incorrect. Email: ${email}. IP: ${req.ip}.` + ); + } + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "Username or password is incorrect" + ) + ); + } if (user.type !== UserType.Internal) { return next( @@ -58,7 +93,10 @@ export async function requestTotpSecret( } try { - const validPassword = await verifyPassword(password, user.passwordHash!); + const validPassword = await verifyPassword( + password, + user.passwordHash! + ); if (!validPassword) { return next(unauthorized()); } diff --git a/server/routers/auth/securityKey.ts b/server/routers/auth/securityKey.ts new file mode 100644 index 00000000..dad3c692 --- /dev/null +++ b/server/routers/auth/securityKey.ts @@ -0,0 +1,717 @@ +import { Request, Response, NextFunction } from "express"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; +import { fromError } from "zod-validation-error"; +import { z } from "zod"; +import { db } from "@server/db"; +import { User, securityKeys, users, webauthnChallenge } from "@server/db"; +import { eq, and, lt } from "drizzle-orm"; +import { response } from "@server/lib"; +import logger from "@server/logger"; +import { + generateRegistrationOptions, + verifyRegistrationResponse, + generateAuthenticationOptions, + verifyAuthenticationResponse +} from "@simplewebauthn/server"; +import type { + GenerateRegistrationOptionsOpts, + VerifyRegistrationResponseOpts, + GenerateAuthenticationOptionsOpts, + VerifyAuthenticationResponseOpts, + VerifiedRegistrationResponse, + VerifiedAuthenticationResponse +} from "@simplewebauthn/server"; +import type { + AuthenticatorTransport, + AuthenticatorTransportFuture, + PublicKeyCredentialDescriptorJSON, + PublicKeyCredentialDescriptorFuture +} from "@simplewebauthn/types"; +import config from "@server/lib/config"; +import { UserType } from "@server/types/UserTypes"; +import { verifyPassword } from "@server/auth/password"; +import { unauthorized } from "@server/auth/unauthorizedResponse"; +import { verifyTotpCode } from "@server/auth/totp"; + +// The RP ID is the domain name of your application +const rpID = (() => { + const url = new URL(config.getRawConfig().app.dashboard_url); + // For localhost, we must use 'localhost' without port + if (url.hostname === 'localhost') { + return 'localhost'; + } + return url.hostname; +})(); + +const rpName = "Pangolin"; +const origin = config.getRawConfig().app.dashboard_url; + +// Database-based challenge storage (replaces in-memory storage) +// Challenges are stored in the webauthnChallenge table with automatic expiration +// This supports clustered deployments and persists across server restarts + +// Clean up expired challenges every 5 minutes +setInterval(async () => { + try { + const now = Date.now(); + await db + .delete(webauthnChallenge) + .where(lt(webauthnChallenge.expiresAt, now)); + logger.debug("Cleaned up expired security key challenges"); + } catch (error) { + logger.error("Failed to clean up expired security key challenges", error); + } +}, 5 * 60 * 1000); + +// Helper functions for challenge management +async function storeChallenge(sessionId: string, challenge: string, securityKeyName?: string, userId?: string) { + const expiresAt = Date.now() + (5 * 60 * 1000); // 5 minutes + + // Delete any existing challenge for this session + await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); + + // Insert new challenge + await db.insert(webauthnChallenge).values({ + sessionId, + challenge, + securityKeyName, + userId, + expiresAt + }); +} + +async function getChallenge(sessionId: string) { + const [challengeData] = await db + .select() + .from(webauthnChallenge) + .where(eq(webauthnChallenge.sessionId, sessionId)) + .limit(1); + + if (!challengeData) { + return null; + } + + // Check if expired + if (challengeData.expiresAt < Date.now()) { + await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); + return null; + } + + return challengeData; +} + +async function clearChallenge(sessionId: string) { + await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); +} + +export const registerSecurityKeyBody = z.object({ + name: z.string().min(1), + password: z.string().min(1), + code: z.string().optional() +}).strict(); + +export const verifyRegistrationBody = z.object({ + credential: z.any() +}).strict(); + +export const startAuthenticationBody = z.object({ + email: z.string().email().optional() +}).strict(); + +export const verifyAuthenticationBody = z.object({ + credential: z.any() +}).strict(); + +export const deleteSecurityKeyBody = z.object({ + password: z.string().min(1), + code: z.string().optional() +}).strict(); + +export async function startRegistration( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = registerSecurityKeyBody.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { name, password, code } = parsedBody.data; + const user = req.user as User; + + // Only allow internal users to use security keys + if (user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Security keys are only available for internal users" + ) + ); + } + + try { + // Verify password + const validPassword = await verifyPassword(password, user.passwordHash!); + if (!validPassword) { + return next(unauthorized()); + } + + // If user has 2FA enabled, require and verify the code + if (user.twoFactorEnabled) { + if (!code) { + return response<{ codeRequested: boolean }>(res, { + data: { codeRequested: true }, + success: true, + error: false, + message: "Two-factor authentication required", + status: HttpCode.ACCEPTED + }); + } + + const validOTP = await verifyTotpCode( + code, + user.twoFactorSecret!, + user.userId + ); + + if (!validOTP) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Two-factor code incorrect. Email: ${user.email}. IP: ${req.ip}.` + ); + } + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "The two-factor code you entered is incorrect" + ) + ); + } + } + + // Get existing security keys for user + const existingSecurityKeys = await db + .select() + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); + + const excludeCredentials = existingSecurityKeys.map(key => ({ + id: new Uint8Array(Buffer.from(key.credentialId, 'base64')), + type: 'public-key' as const, + transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined + })); + + const options: GenerateRegistrationOptionsOpts = { + rpName, + rpID, + userID: user.userId, + userName: user.email || user.username, + attestationType: 'none', + excludeCredentials, + authenticatorSelection: { + residentKey: 'preferred', + userVerification: 'preferred', + } + }; + + const registrationOptions = await generateRegistrationOptions(options); + + // Store challenge in database + await storeChallenge(req.session.sessionId, registrationOptions.challenge, name, user.userId); + + return response(res, { + data: registrationOptions, + success: true, + error: false, + message: "Registration options generated successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to start registration" + ) + ); + } +} + +export async function verifyRegistration( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = verifyRegistrationBody.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { credential } = parsedBody.data; + const user = req.user as User; + + // Only allow internal users to use security keys + if (user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Security keys are only available for internal users" + ) + ); + } + + try { + // Get challenge from database + const challengeData = await getChallenge(req.session.sessionId); + + if (!challengeData) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "No challenge found in session or challenge expired" + ) + ); + } + + const verification = await verifyRegistrationResponse({ + response: credential, + expectedChallenge: challengeData.challenge, + expectedOrigin: origin, + expectedRPID: rpID, + requireUserVerification: false + }); + + const { verified, registrationInfo } = verification; + + if (!verified || !registrationInfo) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Verification failed" + ) + ); + } + + // Store the security key in the database + await db.insert(securityKeys).values({ + credentialId: Buffer.from(registrationInfo.credentialID).toString('base64'), + userId: user.userId, + publicKey: Buffer.from(registrationInfo.credentialPublicKey).toString('base64'), + signCount: registrationInfo.counter || 0, + transports: credential.response.transports ? JSON.stringify(credential.response.transports) : null, + name: challengeData.securityKeyName, + lastUsed: new Date().toISOString(), + dateCreated: new Date().toISOString() + }); + + // Clear challenge data + await clearChallenge(req.session.sessionId); + + return response(res, { + data: null, + success: true, + error: false, + message: "Security key registered successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to verify registration" + ) + ); + } +} + +export async function listSecurityKeys( + req: Request, + res: Response, + next: NextFunction +): Promise { + const user = req.user as User; + + // Only allow internal users to use security keys + if (user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Security keys are only available for internal users" + ) + ); + } + + try { + const userSecurityKeys = await db + .select() + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); + + return response(res, { + data: userSecurityKeys, + success: true, + error: false, + message: "Security keys retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to retrieve security keys" + ) + ); + } +} + +export async function deleteSecurityKey( + req: Request, + res: Response, + next: NextFunction +): Promise { + const { credentialId: encodedCredentialId } = req.params; + const credentialId = decodeURIComponent(encodedCredentialId); + const user = req.user as User; + + const parsedBody = deleteSecurityKeyBody.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { password, code } = parsedBody.data; + + // Only allow internal users to use security keys + if (user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Security keys are only available for internal users" + ) + ); + } + + try { + // Verify password + const validPassword = await verifyPassword(password, user.passwordHash!); + if (!validPassword) { + return next(unauthorized()); + } + + // If user has 2FA enabled, require and verify the code + if (user.twoFactorEnabled) { + if (!code) { + return response<{ codeRequested: boolean }>(res, { + data: { codeRequested: true }, + success: true, + error: false, + message: "Two-factor authentication required", + status: HttpCode.ACCEPTED + }); + } + + const validOTP = await verifyTotpCode( + code, + user.twoFactorSecret!, + user.userId + ); + + if (!validOTP) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Two-factor code incorrect. Email: ${user.email}. IP: ${req.ip}.` + ); + } + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "The two-factor code you entered is incorrect" + ) + ); + } + } + + await db + .delete(securityKeys) + .where(and( + eq(securityKeys.credentialId, credentialId), + eq(securityKeys.userId, user.userId) + )); + + return response(res, { + data: null, + success: true, + error: false, + message: "Security key deleted successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to delete security key" + ) + ); + } +} + +export async function startAuthentication( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = startAuthenticationBody.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { email } = parsedBody.data; + + try { + let allowCredentials: PublicKeyCredentialDescriptorFuture[] = []; + let userId; + + // If email is provided, get security keys for that specific user + if (email) { + const [user] = await db + .select() + .from(users) + .where(eq(users.email, email)) + .limit(1); + + if (!user || user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Invalid credentials" + ) + ); + } + + userId = user.userId; + + const userSecurityKeys = await db + .select() + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); + + if (userSecurityKeys.length === 0) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "No security keys registered for this user" + ) + ); + } + + allowCredentials = userSecurityKeys.map(key => ({ + id: new Uint8Array(Buffer.from(key.credentialId, 'base64')), + type: 'public-key' as const, + transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined + })); + } else { + // If no email provided, allow any security key (for resident key authentication) + allowCredentials = []; + } + + const options: GenerateAuthenticationOptionsOpts = { + rpID, + allowCredentials, + userVerification: 'preferred', + }; + + const authenticationOptions = await generateAuthenticationOptions(options); + + // Generate a temporary session ID for unauthenticated users + const tempSessionId = email ? `temp_${email}_${Date.now()}` : `temp_${Date.now()}`; + + // Store challenge in database + await storeChallenge(tempSessionId, authenticationOptions.challenge, undefined, userId); + + return response(res, { + data: { ...authenticationOptions, tempSessionId }, + success: true, + error: false, + message: "Authentication options generated", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to generate authentication options" + ) + ); + } +} + +export async function verifyAuthentication( + req: Request, + res: Response, + next: NextFunction +): Promise { + const parsedBody = verifyAuthenticationBody.safeParse(req.body); + + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { credential } = parsedBody.data; + const tempSessionId = req.headers['x-temp-session-id'] as string; + + if (!tempSessionId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Your session information is missing. This might happen if you've been inactive for too long or if your browser cleared temporary data. Please start the sign-in process again." + ) + ); + } + + try { + // Get challenge from database + const challengeData = await getChallenge(tempSessionId); + + if (!challengeData) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Your sign-in session has expired. For security reasons, you have 5 minutes to complete the authentication process. Please try signing in again." + ) + ); + } + + // Find the security key in database + const credentialId = Buffer.from(credential.id, 'base64').toString('base64'); + const [securityKey] = await db + .select() + .from(securityKeys) + .where(eq(securityKeys.credentialId, credentialId)) + .limit(1); + + if (!securityKey) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "We couldn't verify your security key. This might happen if your device isn't compatible or if the security key was removed too quickly. Please try again and keep your security key connected until the process completes." + ) + ); + } + + // Get the user + const [user] = await db + .select() + .from(users) + .where(eq(users.userId, securityKey.userId)) + .limit(1); + + if (!user || user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "User not found or not authorized for security key authentication" + ) + ); + } + + const verification = await verifyAuthenticationResponse({ + response: credential, + expectedChallenge: challengeData.challenge, + expectedOrigin: origin, + expectedRPID: rpID, + authenticator: { + credentialID: Buffer.from(securityKey.credentialId, 'base64'), + credentialPublicKey: Buffer.from(securityKey.publicKey, 'base64'), + counter: securityKey.signCount, + transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransportFuture[] : undefined + }, + requireUserVerification: false + }); + + const { verified, authenticationInfo } = verification; + + if (!verified) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Authentication failed. This could happen if your security key wasn't recognized or was removed too early. Please ensure your security key is properly connected and try again." + ) + ); + } + + // Update sign count + await db + .update(securityKeys) + .set({ + signCount: authenticationInfo.newCounter, + lastUsed: new Date().toISOString() + }) + .where(eq(securityKeys.credentialId, credentialId)); + + // Create session for the user + const { createSession, generateSessionToken, serializeSessionCookie } = await import("@server/auth/sessions/app"); + const token = generateSessionToken(); + const session = await createSession(token, user.userId); + const isSecure = req.protocol === "https"; + const cookie = serializeSessionCookie( + token, + isSecure, + new Date(session.expiresAt) + ); + + res.setHeader("Set-Cookie", cookie); + + // Clear challenge data + await clearChallenge(tempSessionId); + + return response(res, { + data: null, + success: true, + error: false, + message: "Authentication successful", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to verify authentication" + ) + ); + } +} \ No newline at end of file diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 70018a7d..6b45a93e 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -6,18 +6,22 @@ import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; import { twoFactorBackupCodes, User, users } from "@server/db"; -import { eq } from "drizzle-orm"; -import { alphabet, generateRandomString } from "oslo/crypto"; -import { hashPassword } from "@server/auth/password"; +import { eq, and } from "drizzle-orm"; +import { hashPassword, verifyPassword } from "@server/auth/password"; import { verifyTotpCode } from "@server/auth/totp"; import logger from "@server/logger"; import { sendEmail } from "@server/emails"; import TwoFactorAuthNotification from "@server/emails/templates/TwoFactorAuthNotification"; import config from "@server/lib/config"; import { UserType } from "@server/types/UserTypes"; +import { generateBackupCodes } from "@server/lib/totp"; +import { verifySession } from "@server/auth/sessions/verifySession"; +import { unauthorized } from "@server/auth/unauthorizedResponse"; export const verifyTotpBody = z .object({ + email: z.string().email().optional(), + password: z.string().optional(), code: z.string() }) .strict(); @@ -45,38 +49,83 @@ export async function verifyTotp( ); } - const { code } = parsedBody.data; - - const user = req.user as User; - - if (user.type !== UserType.Internal) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Two-factor authentication is not supported for external users" - ) - ); - } - - if (user.twoFactorEnabled) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "Two-factor authentication is already enabled" - ) - ); - } - - if (!user.twoFactorSecret) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - "User has not requested two-factor authentication" - ) - ); - } + const { code, email, password } = parsedBody.data; try { + const { user: sessionUser, session: existingSession } = + await verifySession(req); + + let user: User | null = sessionUser; + if (!existingSession) { + if (!email || !password) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Email and password are required for two-factor authentication" + ) + ); + } + const [res] = await db + .select() + .from(users) + .where( + and( + eq(users.type, UserType.Internal), + eq(users.email, email) + ) + ); + user = res; + + const validPassword = await verifyPassword( + password, + user.passwordHash! + ); + if (!validPassword) { + return next(unauthorized()); + } + } + + if (!user) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Username or password incorrect. Email: ${email}. IP: ${req.ip}.` + ); + } + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "Username or password is incorrect" + ) + ); + } + + if (user.type !== UserType.Internal) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Two-factor authentication is not supported for external users" + ) + ); + } + + if (user.twoFactorEnabled) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Two-factor authentication is already enabled" + ) + ); + } + + if (!user.twoFactorSecret) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "User has not requested two-factor authentication" + ) + ); + } + const valid = await verifyTotpCode( code, user.twoFactorSecret, @@ -89,7 +138,9 @@ export async function verifyTotp( await db.transaction(async (trx) => { await trx .update(users) - .set({ twoFactorEnabled: true }) + .set({ + twoFactorEnabled: true + }) .where(eq(users.userId, user.userId)); const backupCodes = await generateBackupCodes(); @@ -153,12 +204,3 @@ export async function verifyTotp( ); } } - -async function generateBackupCodes(): Promise { - const codes = []; - for (let i = 0; i < 10; i++) { - const code = generateRandomString(6, alphabet("0-9", "A-Z", "a-z")); - codes.push(code); - } - return codes; -} diff --git a/server/routers/external.ts b/server/routers/external.ts index 0f51dc50..4ad890cb 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -535,6 +535,7 @@ unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo); unauthenticated.get("/user", verifySessionMiddleware, user.getUser); authenticated.get("/users", verifyUserIsServerAdmin, user.adminListUsers); +authenticated.get("/user/:userId", verifyUserIsServerAdmin, user.adminGetUser); authenticated.delete( "/user/:userId", verifyUserIsServerAdmin, @@ -550,6 +551,12 @@ authenticated.put( authenticated.get("/org/:orgId/user/:userId", verifyOrgAccess, user.getOrgUser); +authenticated.post( + "/user/:userId/2fa", + verifyUserIsServerAdmin, + user.updateUser2FA +); + authenticated.get( "/org/:orgId/users", verifyOrgAccess, @@ -795,12 +802,8 @@ authRouter.post("/logout", auth.logout); authRouter.post("/newt/get-token", getNewtToken); authRouter.post("/olm/get-token", getOlmToken); -authRouter.post("/2fa/enable", verifySessionUserMiddleware, auth.verifyTotp); -authRouter.post( - "/2fa/request", - verifySessionUserMiddleware, - auth.requestTotpSecret -); +authRouter.post("/2fa/enable", auth.verifyTotp); +authRouter.post("/2fa/request", auth.requestTotpSecret); authRouter.post("/2fa/disable", verifySessionUserMiddleware, auth.disable2fa); authRouter.post("/verify-email", verifySessionMiddleware, auth.verifyEmail); @@ -874,3 +877,36 @@ authRouter.post("/idp/:idpId/oidc/validate-callback", idp.validateOidcCallback); authRouter.put("/set-server-admin", auth.setServerAdmin); authRouter.get("/initial-setup-complete", auth.initialSetupComplete); + +// Security Key routes +authRouter.post( + "/security-key/register/start", + verifySessionUserMiddleware, + rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // Allow 5 security key registrations per 15 minutes + keyGenerator: (req) => `securityKeyRegister:${req.user?.userId}`, + handler: (req, res, next) => { + const message = `You can only register ${5} security keys every ${15} minutes. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + } + }), + auth.startRegistration +); +authRouter.post("/security-key/register/verify", verifySessionUserMiddleware, auth.verifyRegistration); +authRouter.post( + "/security-key/authenticate/start", + rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 10, // Allow 10 authentication attempts per 15 minutes per IP + keyGenerator: (req) => `securityKeyAuth:${req.ip}`, + handler: (req, res, next) => { + const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`; + return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); + } + }), + auth.startAuthentication +); +authRouter.post("/security-key/authenticate/verify", auth.verifyAuthentication); +authRouter.get("/security-key/list", verifySessionUserMiddleware, auth.listSecurityKeys); +authRouter.delete("/security-key/:credentialId", verifySessionUserMiddleware, auth.deleteSecurityKey); diff --git a/server/routers/integration.ts b/server/routers/integration.ts index fc66a88d..51604a11 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -381,6 +381,20 @@ authenticated.get( user.getOrgUser ); +authenticated.post( + "/user/:userId/2fa", + verifyApiKeyIsRoot, + verifyApiKeyHasAction(ActionsEnum.updateUser), + user.updateUser2FA +); + +authenticated.get( + "/user/:userId", + verifyApiKeyIsRoot, + verifyApiKeyHasAction(ActionsEnum.getUser), + user.adminGetUser +); + authenticated.get( "/org/:orgId/users", verifyApiKeyOrgAccess, diff --git a/server/routers/user/adminGetUser.ts b/server/routers/user/adminGetUser.ts new file mode 100644 index 00000000..0a961bec --- /dev/null +++ b/server/routers/user/adminGetUser.ts @@ -0,0 +1,94 @@ +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { idp, users } from "@server/db"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { OpenAPITags, registry } from "@server/openApi"; + +const adminGetUserSchema = z + .object({ + userId: z.string().min(1) + }) + .strict(); + +registry.registerPath({ + method: "get", + path: "/user/{userId}", + description: "Get a user by ID.", + tags: [OpenAPITags.User], + request: { + params: adminGetUserSchema + }, + responses: {} +}); + +async function queryUser(userId: string) { + const [user] = await db + .select({ + userId: users.userId, + email: users.email, + username: users.username, + name: users.name, + type: users.type, + twoFactorEnabled: users.twoFactorEnabled, + twoFactorSetupRequested: users.twoFactorSetupRequested, + emailVerified: users.emailVerified, + serverAdmin: users.serverAdmin, + idpName: idp.name, + idpId: users.idpId, + dateCreated: users.dateCreated + }) + .from(users) + .leftJoin(idp, eq(users.idpId, idp.idpId)) + .where(eq(users.userId, userId)) + .limit(1); + return user; +} + +export type AdminGetUserResponse = NonNullable< + Awaited> +>; + +export async function adminGetUser( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = adminGetUserSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid user ID") + ); + } + const { userId } = parsedParams.data; + + const user = await queryUser(userId); + + if (!user) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `User with ID ${userId} not found` + ) + ); + } + + return response(res, { + data: user, + success: true, + error: false, + message: "User retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/user/adminListUsers.ts b/server/routers/user/adminListUsers.ts index cb1e21fb..308b9def 100644 --- a/server/routers/user/adminListUsers.ts +++ b/server/routers/user/adminListUsers.ts @@ -37,7 +37,9 @@ async function queryUsers(limit: number, offset: number) { serverAdmin: users.serverAdmin, type: users.type, idpName: idp.name, - idpId: users.idpId + idpId: users.idpId, + twoFactorEnabled: users.twoFactorEnabled, + twoFactorSetupRequested: users.twoFactorSetupRequested }) .from(users) .leftJoin(idp, eq(users.idpId, idp.idpId)) diff --git a/server/routers/user/adminUpdateUser2FA.ts b/server/routers/user/adminUpdateUser2FA.ts new file mode 100644 index 00000000..becd2091 --- /dev/null +++ b/server/routers/user/adminUpdateUser2FA.ts @@ -0,0 +1,133 @@ +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { users, userOrgs } from "@server/db"; +import { eq, and } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromError } from "zod-validation-error"; +import { OpenAPITags, registry } from "@server/openApi"; + +const updateUser2FAParamsSchema = z + .object({ + userId: z.string() + }) + .strict(); + +const updateUser2FABodySchema = z + .object({ + twoFactorSetupRequested: z.boolean() + }) + .strict(); + +export type UpdateUser2FAResponse = { + userId: string; + twoFactorRequested: boolean; +}; + +registry.registerPath({ + method: "post", + path: "/user/{userId}/2fa", + description: "Update a user's 2FA status.", + tags: [OpenAPITags.User], + request: { + params: updateUser2FAParamsSchema, + body: { + content: { + "application/json": { + schema: updateUser2FABodySchema + } + } + } + }, + responses: {} +}); + +export async function updateUser2FA( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + const parsedParams = updateUser2FAParamsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = updateUser2FABodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { userId } = parsedParams.data; + const { twoFactorSetupRequested } = parsedBody.data; + + // Verify the user exists in the organization + const existingUser = await db + .select() + .from(users) + .where(eq(users.userId, userId)) + .limit(1); + + if (existingUser.length === 0) { + return next(createHttpError(HttpCode.NOT_FOUND, "User not found")); + } + + if (existingUser[0].type !== "internal") { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Two-factor authentication is not supported for external users" + ) + ); + } + + logger.debug(`Updating 2FA for user ${userId} to ${twoFactorSetupRequested}`); + + if (twoFactorSetupRequested) { + await db + .update(users) + .set({ + twoFactorSetupRequested: true, + }) + .where(eq(users.userId, userId)); + } else { + await db + .update(users) + .set({ + twoFactorSetupRequested: false, + twoFactorEnabled: false, + twoFactorSecret: null + }) + .where(eq(users.userId, userId)); + } + + return response(res, { + data: { + userId: existingUser[0].userId, + twoFactorRequested: twoFactorSetupRequested + }, + success: true, + error: false, + message: `2FA ${twoFactorSetupRequested ? "enabled" : "disabled"} for user successfully`, + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} diff --git a/server/routers/user/getOrgUser.ts b/server/routers/user/getOrgUser.ts index 562ef34e..05e231c9 100644 --- a/server/routers/user/getOrgUser.ts +++ b/server/routers/user/getOrgUser.ts @@ -23,7 +23,8 @@ async function queryUser(orgId: string, userId: string) { roleId: userOrgs.roleId, roleName: roles.name, isOwner: userOrgs.isOwner, - isAdmin: roles.isAdmin + isAdmin: roles.isAdmin, + twoFactorEnabled: users.twoFactorEnabled, }) .from(userOrgs) .leftJoin(roles, eq(userOrgs.roleId, roles.roleId)) diff --git a/server/routers/user/index.ts b/server/routers/user/index.ts index 49278c14..6d342ad3 100644 --- a/server/routers/user/index.ts +++ b/server/routers/user/index.ts @@ -7,6 +7,9 @@ export * from "./acceptInvite"; export * from "./getOrgUser"; export * from "./adminListUsers"; export * from "./adminRemoveUser"; +export * from "./adminGetUser"; export * from "./listInvitations"; export * from "./removeInvitation"; export * from "./createOrgUser"; +export * from "./adminUpdateUser2FA"; +export * from "./adminGetUser"; diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts index 2e23f401..83c1e492 100644 --- a/server/routers/user/listUsers.ts +++ b/server/routers/user/listUsers.ts @@ -49,7 +49,8 @@ async function queryUsers(orgId: string, limit: number, offset: number) { roleName: roles.name, isOwner: userOrgs.isOwner, idpName: idp.name, - idpId: users.idpId + idpId: users.idpId, + twoFactorEnabled: users.twoFactorEnabled, }) .from(users) .leftJoin(userOrgs, eq(users.userId, userOrgs.userId)) diff --git a/server/setup/migrationsPg.ts b/server/setup/migrationsPg.ts index efc2e5c3..aeec86ff 100644 --- a/server/setup/migrationsPg.ts +++ b/server/setup/migrationsPg.ts @@ -4,7 +4,7 @@ import semver from "semver"; import { versionMigrations } from "../db/pg"; import { __DIRNAME, APP_VERSION } from "@server/lib/consts"; import path from "path"; -import m1 from "./scriptsSqlite/1.6.0"; +import m1 from "./scriptsPg/1.6.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 6a1380f2..dee0dd66 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -262,7 +262,19 @@ export default function ReverseProxyTargets(props: { // make sure that the target IP is within the site subnet const targetIp = data.ip; const subnet = site.subnet; - if (!isIPInSubnet(targetIp, subnet)) { + try { + if (!isIPInSubnet(targetIp, subnet)) { + toast({ + variant: "destructive", + title: t("targetWireGuardErrorInvalidIp"), + description: t( + "targetWireGuardErrorInvalidIpDescription" + ) + }); + return; + } + } catch (error) { + console.error(error); toast({ variant: "destructive", title: t("targetWireGuardErrorInvalidIp"), @@ -885,10 +897,8 @@ function isIPInSubnet(subnet: string, ip: string): boolean { const [subnetIP, maskBits] = subnet.split("/"); const mask = parseInt(maskBits); - const t = useTranslations(); - if (mask < 0 || mask > 32) { - throw new Error(t("subnetMaskErrorInvalid")); + throw new Error("subnetMaskErrorInvalid"); } // Convert IP addresses to binary numbers @@ -905,17 +915,16 @@ function isIPInSubnet(subnet: string, ip: string): boolean { function ipToNumber(ip: string): number { // Validate IP address format const parts = ip.split("."); - const t = useTranslations(); if (parts.length !== 4) { - throw new Error(t("ipAddressErrorInvalidFormat")); + throw new Error("ipAddressErrorInvalidFormat"); } // Convert IP octets to 32-bit number return parts.reduce((num, octet) => { const oct = parseInt(octet); if (isNaN(oct) || oct < 0 || oct > 255) { - throw new Error(t("ipAddressErrorInvalidOctet")); + throw new Error("ipAddressErrorInvalidOctet"); } return (num << 8) + oct; }, 0); diff --git a/src/app/admin/users/AdminUsersTable.tsx b/src/app/admin/users/AdminUsersTable.tsx index c3ceb4dc..6c5e4613 100644 --- a/src/app/admin/users/AdminUsersTable.tsx +++ b/src/app/admin/users/AdminUsersTable.tsx @@ -3,7 +3,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { UsersDataTable } from "./AdminUsersDataTable"; import { Button } from "@app/components/ui/button"; -import { ArrowRight, ArrowUpDown } from "lucide-react"; +import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; @@ -12,6 +12,12 @@ import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; +import { + DropdownMenu, + DropdownMenuItem, + DropdownMenuContent, + DropdownMenuTrigger +} from "@app/components/ui/dropdown-menu"; export type GlobalUserRow = { id: string; @@ -22,6 +28,8 @@ export type GlobalUserRow = { idpId: number | null; idpName: string; dateCreated: string; + twoFactorEnabled: boolean | null; + twoFactorSetupRequested: boolean | null; }; type Props = { @@ -138,23 +146,79 @@ export default function UsersTable({ users }: Props) { ); } }, + { + accessorKey: "twoFactorEnabled", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) => { + const userRow = row.original; + + return ( +
+ + {userRow.twoFactorEnabled || + userRow.twoFactorSetupRequested ? ( + + {t("enabled")} + + ) : ( + {t("disabled")} + )} + +
+ ); + } + }, { id: "actions", cell: ({ row }) => { const r = row.original; return ( <> -
+
+ + + + + + { + setSelected(r); + setIsDeleteModalOpen(true); + }} + > + {t("delete")} + + +
diff --git a/src/app/admin/users/[userId]/general/page.tsx b/src/app/admin/users/[userId]/general/page.tsx new file mode 100644 index 00000000..ae720a6f --- /dev/null +++ b/src/app/admin/users/[userId]/general/page.tsx @@ -0,0 +1,134 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { SwitchInput } from "@app/components/SwitchInput"; +import { Button } from "@app/components/ui/button"; +import { toast } from "@app/hooks/useToast"; +import { formatAxiosError } from "@app/lib/api"; +import { createApiClient } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useTranslations } from "next-intl"; +import { useParams } from "next/navigation"; +import { + SettingsContainer, + SettingsSection, + SettingsSectionHeader, + SettingsSectionTitle, + SettingsSectionDescription, + SettingsSectionBody, + SettingsSectionForm +} from "@app/components/Settings"; +import { UserType } from "@server/types/UserTypes"; + +export default function GeneralPage() { + const { userId } = useParams(); + const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + const [loadingData, setLoadingData] = useState(true); + const [loading, setLoading] = useState(false); + const [twoFactorEnabled, setTwoFactorEnabled] = useState(false); + const [userType, setUserType] = useState(null); + + useEffect(() => { + // Fetch current user 2FA status + const fetchUserData = async () => { + setLoadingData(true); + try { + const response = await api.get(`/user/${userId}`); + if (response.status === 200) { + const userData = response.data.data; + setTwoFactorEnabled( + userData.twoFactorEnabled || + userData.twoFactorSetupRequested + ); + setUserType(userData.type); + } + } catch (error) { + console.error("Failed to fetch user data:", error); + toast({ + variant: "destructive", + title: t("userErrorDelete"), + description: formatAxiosError(error, t("userErrorDelete")) + }); + } + setLoadingData(false); + }; + + fetchUserData(); + }, [userId]); + + const handleTwoFactorToggle = (enabled: boolean) => { + setTwoFactorEnabled(enabled); + }; + + const handleSaveSettings = async () => { + setLoading(true); + + try { + console.log("twoFactorEnabled", twoFactorEnabled); + await api.post(`/user/${userId}/2fa`, { + twoFactorSetupRequested: twoFactorEnabled + }); + + setTwoFactorEnabled(twoFactorEnabled); + } catch (error) { + toast({ + variant: "destructive", + title: t("otpErrorEnable"), + description: formatAxiosError( + error, + t("otpErrorEnableDescription") + ) + }); + } finally { + setLoading(false); + } + }; + + if (loadingData) { + return null; + } + + return ( + <> + + + + + {t("general")} + + + {t("userDescription2")} + + + + + +
+ +
+
+
+
+
+ +
+ +
+ + ); +} diff --git a/src/app/admin/users/[userId]/layout.tsx b/src/app/admin/users/[userId]/layout.tsx new file mode 100644 index 00000000..062b40d8 --- /dev/null +++ b/src/app/admin/users/[userId]/layout.tsx @@ -0,0 +1,55 @@ +import { internal } from "@app/lib/api"; +import { AxiosResponse } from "axios"; +import { redirect } from "next/navigation"; +import { authCookieHeader } from "@app/lib/api/cookies"; +import { AdminGetUserResponse } from "@server/routers/user/adminGetUser"; +import { HorizontalTabs } from "@app/components/HorizontalTabs"; +import { cache } from "react"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; +import { getTranslations } from 'next-intl/server'; + +interface UserLayoutProps { + children: React.ReactNode; + params: Promise<{ userId: string }>; +} + +export default async function UserLayoutProps(props: UserLayoutProps) { + const params = await props.params; + + const { children } = props; + + const t = await getTranslations(); + + let user = null; + try { + const getUser = cache(async () => + internal.get>( + `/user/${params.userId}`, + await authCookieHeader() + ) + ); + const res = await getUser(); + user = res.data.data; + } catch { + redirect(`/admin/users`); + } + + const navItems = [ + { + title: t('general'), + href: "/admin/users/{userId}/general" + } + ]; + + return ( + <> + + + {children} + + + ); +} \ No newline at end of file diff --git a/src/app/admin/users/[userId]/page.tsx b/src/app/admin/users/[userId]/page.tsx new file mode 100644 index 00000000..edf5aaed --- /dev/null +++ b/src/app/admin/users/[userId]/page.tsx @@ -0,0 +1,8 @@ +import { redirect } from "next/navigation"; + +export default async function UserPage(props: { + params: Promise<{ userId: string }>; +}) { + const { userId } = await props.params; + redirect(`/admin/users/${userId}/general`); +} \ No newline at end of file diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 1e29a311..e9673374 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -38,7 +38,9 @@ export default async function UsersPage(props: PageProps) { idpId: row.idpId, idpName: row.idpName || t('idpNameInternal'), dateCreated: row.dateCreated, - serverAdmin: row.serverAdmin + serverAdmin: row.serverAdmin, + twoFactorEnabled: row.twoFactorEnabled, + twoFactorSetupRequested: row.twoFactorSetupRequested }; }); diff --git a/src/app/auth/2fa/setup/page.tsx b/src/app/auth/2fa/setup/page.tsx new file mode 100644 index 00000000..64a6cf57 --- /dev/null +++ b/src/app/auth/2fa/setup/page.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle +} from "@/components/ui/card"; +import TwoFactorSetupForm from "@app/components/TwoFactorSetupForm"; +import { useTranslations } from "next-intl"; +import { cleanRedirect } from "@app/lib/cleanRedirect"; + +export default function Setup2FAPage() { + const router = useRouter(); + const searchParams = useSearchParams(); + const redirect = searchParams?.get("redirect"); + const email = searchParams?.get("email"); + + const t = useTranslations(); + + // Redirect to login if no email is provided + useEffect(() => { + if (!email) { + router.push("/auth/login"); + } + }, [email, router]); + + const handleComplete = () => { + console.log("2FA setup complete", redirect, email); + if (redirect) { + const cleanUrl = cleanRedirect(redirect); + console.log("Redirecting to:", cleanUrl); + router.push(cleanUrl); + } else { + router.push("/"); + } + }; + + return ( +
+ + + {t("otpSetup")} + + {t("adminEnabled2FaOnYourAccount", { email: email || "your account" })} + + + + + + +
+ ); +} diff --git a/src/components/Enable2FaDialog.tsx b/src/components/Enable2FaDialog.tsx new file mode 100644 index 00000000..0fca0085 --- /dev/null +++ b/src/components/Enable2FaDialog.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { useState, useRef } from "react"; +import { Button } from "@/components/ui/button"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import TwoFactorSetupForm from "./TwoFactorSetupForm"; +import { useTranslations } from "next-intl"; +import { useUserContext } from "@app/hooks/useUserContext"; + +type Enable2FaDialogProps = { + open: boolean; + setOpen: (val: boolean) => void; +}; + +export default function Enable2FaDialog({ open, setOpen }: Enable2FaDialogProps) { + const t = useTranslations(); + const [currentStep, setCurrentStep] = useState(1); + const [loading, setLoading] = useState(false); + const formRef = useRef<{ handleSubmit: () => void }>(null); + const { user, updateUser } = useUserContext(); + + function reset() { + setCurrentStep(1); + setLoading(false); + } + + const handleSubmit = () => { + if (formRef.current) { + formRef.current.handleSubmit(); + } + }; + + return ( + { + setOpen(val); + reset(); + }} + > + + + + {t('otpSetup')} + + + {t('otpSetupDescription')} + + + + {setOpen(false); updateUser({ twoFactorEnabled: true });}} + onStepChange={setCurrentStep} + onLoadingChange={setLoading} + /> + + + + + + {(currentStep === 1 || currentStep === 2) && ( + + )} + + + + ); +} \ No newline at end of file diff --git a/src/components/Enable2FaForm.tsx b/src/components/Enable2FaForm.tsx index 47cfb8e8..acc00400 100644 --- a/src/components/Enable2FaForm.tsx +++ b/src/components/Enable2FaForm.tsx @@ -1,46 +1,7 @@ "use client"; -import { useState } from "react"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { AlertCircle, CheckCircle2 } from "lucide-react"; -import { createApiClient } from "@app/lib/api"; -import { useEnvContext } from "@app/hooks/useEnvContext"; -import { AxiosResponse } from "axios"; -import { - RequestTotpSecretBody, - RequestTotpSecretResponse, - VerifyTotpBody, - VerifyTotpResponse -} from "@server/routers/auth"; -import { z } from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage -} from "@app/components/ui/form"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle -} from "@app/components/Credenza"; -import { toast } from "@app/hooks/useToast"; -import { formatAxiosError } from "@app/lib/api"; -import CopyTextBox from "@app/components/CopyTextBox"; -import { QRCodeCanvas, QRCodeSVG } from "qrcode.react"; import { useUserContext } from "@app/hooks/useUserContext"; -import { useTranslations } from "next-intl"; +import Enable2FaDialog from "./Enable2FaDialog"; type Enable2FaProps = { open: boolean; @@ -48,261 +9,5 @@ type Enable2FaProps = { }; export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) { - const [step, setStep] = useState(1); - const [secretKey, setSecretKey] = useState(""); - const [secretUri, setSecretUri] = useState(""); - const [verificationCode, setVerificationCode] = useState(""); - const [error, setError] = useState(""); - const [success, setSuccess] = useState(false); - const [loading, setLoading] = useState(false); - const [backupCodes, setBackupCodes] = useState([]); - - const { user, updateUser } = useUserContext(); - - const api = createApiClient(useEnvContext()); - const t = useTranslations(); - - const enableSchema = z.object({ - password: z.string().min(1, { message: t('passwordRequired') }) - }); - - const confirmSchema = z.object({ - code: z.string().length(6, { message: t('pincodeInvalid') }) - }); - - const enableForm = useForm>({ - resolver: zodResolver(enableSchema), - defaultValues: { - password: "" - } - }); - - const confirmForm = useForm>({ - resolver: zodResolver(confirmSchema), - defaultValues: { - code: "" - } - }); - - const request2fa = async (values: z.infer) => { - setLoading(true); - - const res = await api - .post>( - `/auth/2fa/request`, - { - password: values.password - } as RequestTotpSecretBody - ) - .catch((e) => { - toast({ - title: t('otpErrorEnable'), - description: formatAxiosError( - e, - t('otpErrorEnableDescription') - ), - variant: "destructive" - }); - }); - - if (res && res.data.data.secret) { - setSecretKey(res.data.data.secret); - setSecretUri(res.data.data.uri); - setStep(2); - } - - setLoading(false); - }; - - const confirm2fa = async (values: z.infer) => { - setLoading(true); - - const res = await api - .post>(`/auth/2fa/enable`, { - code: values.code - } as VerifyTotpBody) - .catch((e) => { - toast({ - title: t('otpErrorEnable'), - description: formatAxiosError( - e, - t('otpErrorEnableDescription') - ), - variant: "destructive" - }); - }); - - if (res && res.data.data.valid) { - setBackupCodes(res.data.data.backupCodes || []); - updateUser({ twoFactorEnabled: true }); - setStep(3); - } - - setLoading(false); - }; - - const handleVerify = () => { - if (verificationCode.length !== 6) { - setError(t('otpSetupCheckCode')); - return; - } - if (verificationCode === "123456") { - setSuccess(true); - setStep(3); - } else { - setError(t('otpSetupCheckCodeRetry')); - } - }; - - function reset() { - setLoading(false); - setStep(1); - setSecretKey(""); - setSecretUri(""); - setVerificationCode(""); - setError(""); - setSuccess(false); - setBackupCodes([]); - enableForm.reset(); - confirmForm.reset(); - } - - return ( - { - setOpen(val); - reset(); - }} - > - - - - {t('otpSetup')} - - - {t('otpSetupDescription')} - - - - {step === 1 && ( -
- -
- ( - - {t('password')} - - - - - - )} - /> -
-
- - )} - - {step === 2 && ( -
-

- {t('otpSetupScanQr')} -

-
- -
-
- -
- -
- -
- ( - - - {t('otpSetupSecretCode')} - - - - - - - )} - /> -
-
- -
- )} - - {step === 3 && ( -
- -

- {t('otpSetupSuccess')} -

-

- {t('otpSetupSuccessStoreBackupCodes')} -

- -
- -
-
- )} -
- - - - - {(step === 1 || step === 2) && ( - - )} - -
-
- ); + return ; } diff --git a/src/components/LocaleSwitcher.tsx b/src/components/LocaleSwitcher.tsx index 9c47ae18..0e883a3c 100644 --- a/src/components/LocaleSwitcher.tsx +++ b/src/components/LocaleSwitcher.tsx @@ -48,6 +48,10 @@ export default function LocaleSwitcher() { { value: "zh-CN", label: "简体中文" + }, + { + value: "ko-KR", + label: "한국어" } ]} /> diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 14189c37..04ed25fb 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -4,8 +4,8 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +import { Button } from "@app/components/ui/button"; +import { Input } from "@app/components/ui/input"; import { Form, FormControl, @@ -13,20 +13,20 @@ import { FormItem, FormLabel, FormMessage -} from "@/components/ui/form"; +} from "@app/components/ui/form"; import { Card, CardContent, CardDescription, CardHeader, CardTitle -} from "@/components/ui/card"; -import { Alert, AlertDescription } from "@/components/ui/alert"; +} from "@app/components/ui/card"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; import { LoginResponse } from "@server/routers/auth"; import { useRouter } from "next/navigation"; import { AxiosResponse } from "axios"; import { formatAxiosError } from "@app/lib/api"; -import { LockIcon } from "lucide-react"; +import { LockIcon, FingerprintIcon } from "lucide-react"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { @@ -41,6 +41,7 @@ import Image from "next/image"; import { GenerateOidcUrlResponse } from "@server/routers/idp"; import { Separator } from "./ui/separator"; import { useTranslations } from "next-intl"; +import { startAuthentication } from "@simplewebauthn/browser"; export type LoginFormIDP = { idpId: number; @@ -65,6 +66,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const hasIdp = idps && idps.length > 0; const [mfaRequested, setMfaRequested] = useState(false); + const [showSecurityKeyPrompt, setShowSecurityKeyPrompt] = useState(false); const t = useTranslations(); @@ -94,30 +96,104 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { } }); + async function initiateSecurityKeyAuth() { + setShowSecurityKeyPrompt(true); + setLoading(true); + setError(null); + + try { + // Start WebAuthn authentication without email + const startRes = await api.post("/auth/security-key/authenticate/start", {}); + + if (!startRes) { + setError(t('securityKeyAuthError', { + defaultValue: "Failed to start security key authentication" + })); + return; + } + + const { tempSessionId, ...options } = startRes.data.data; + + // Perform WebAuthn authentication + try { + const credential = await startAuthentication(options); + + // Verify authentication + const verifyRes = await api.post( + "/auth/security-key/authenticate/verify", + { credential }, + { + headers: { + 'X-Temp-Session-Id': tempSessionId + } + } + ); + + if (verifyRes) { + if (onLogin) { + await onLogin(); + } + } + } catch (error: any) { + if (error.name === 'NotAllowedError') { + if (error.message.includes('denied permission')) { + setError(t('securityKeyPermissionDenied', { + defaultValue: "Please allow access to your security key to continue signing in." + })); + } else { + setError(t('securityKeyRemovedTooQuickly', { + defaultValue: "Please keep your security key connected until the sign-in process completes." + })); + } + } else if (error.name === 'NotSupportedError') { + setError(t('securityKeyNotSupported', { + defaultValue: "Your security key may not be compatible. Please try a different security key." + })); + } else { + setError(t('securityKeyUnknownError', { + defaultValue: "There was a problem using your security key. Please try again." + })); + } + } + } catch (e: any) { + if (e.isAxiosError) { + setError(formatAxiosError(e, t('securityKeyAuthError', { + defaultValue: "Failed to authenticate with security key" + }))); + } else { + console.error(e); + setError(e.message || t('securityKeyAuthError', { + defaultValue: "Failed to authenticate with security key" + })); + } + } finally { + setLoading(false); + setShowSecurityKeyPrompt(false); + } + } + async function onSubmit(values: any) { const { email, password } = form.getValues(); const { code } = mfaForm.getValues(); setLoading(true); + setError(null); + setShowSecurityKeyPrompt(false); - const res = await api - .post>("/auth/login", { + try { + const res = await api.post>("/auth/login", { email, password, code - }) - .catch((e) => { - console.error(e); - setError( - formatAxiosError(e, t('loginError')) - ); }); - if (res) { - setError(null); - const data = res.data.data; + if (data?.useSecurityKey) { + await initiateSecurityKeyAuth(); + return; + } + if (data?.codeRequested) { setMfaRequested(true); setLoading(false); @@ -134,12 +210,32 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { return; } + if (data?.twoFactorSetupRequired) { + const setupUrl = `/auth/2fa/setup?email=${encodeURIComponent(email)}${redirect ? `&redirect=${encodeURIComponent(redirect)}` : ''}`; + router.push(setupUrl); + return; + } + if (onLogin) { await onLogin(); } + } catch (e: any) { + if (e.isAxiosError) { + const errorMessage = formatAxiosError(e, t('loginError', { + defaultValue: "Failed to log in" + })); + setError(errorMessage); + return; + } else { + console.error(e); + setError(e.message || t('loginError', { + defaultValue: "Failed to log in" + })); + return; + } + } finally { + setLoading(false); } - - setLoading(false); } async function loginWithIdp(idpId: number) { @@ -167,6 +263,17 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { return (
+ {showSecurityKeyPrompt && ( + + + + {t('securityKeyPrompt', { + defaultValue: "Please verify your identity using your security key. Make sure your security key is connected and ready." + })} + + + )} + {!mfaRequested && ( <>
@@ -216,6 +323,16 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) {
+ +
+ +
@@ -250,9 +367,9 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { pattern={ REGEXP_ONLY_DIGITS_AND_CHARS } - onChange={(e) => { - field.onChange(e); - if (e.length === 6) { + onChange={(value: string) => { + field.onChange(value); + if (value.length === 6) { mfaForm.handleSubmit(onSubmit)(); } }} @@ -311,14 +428,17 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { {!mfaRequested && ( <> {hasIdp && ( diff --git a/src/components/PermissionsSelectBox.tsx b/src/components/PermissionsSelectBox.tsx index 3f225dee..6f11d98e 100644 --- a/src/components/PermissionsSelectBox.tsx +++ b/src/components/PermissionsSelectBox.tsx @@ -23,7 +23,10 @@ function getActionsCategories(root: boolean) { [t('actionGetOrg')]: "getOrg", [t('actionUpdateOrg')]: "updateOrg", [t('actionGetOrgUser')]: "getOrgUser", - [t('actionListOrgDomains')]: "listOrgDomains", + [t('actionInviteUser')]: "inviteUser", + [t('actionRemoveUser')]: "removeUser", + [t('actionListUsers')]: "listUsers", + [t('actionListOrgDomains')]: "listOrgDomains" }, Site: { @@ -65,16 +68,9 @@ function getActionsCategories(root: boolean) { [t('actionGetRole')]: "getRole", [t('actionListRole')]: "listRoles", [t('actionUpdateRole')]: "updateRole", - [t('actionListAllowedRoleResources')]: "listRoleResources" - }, - - User: { - [t('actionInviteUser')]: "inviteUser", - [t('actionRemoveUser')]: "removeUser", - [t('actionListUsers')]: "listUsers", + [t('actionListAllowedRoleResources')]: "listRoleResources", [t('actionAddUserRole')]: "addUserRole" }, - "Access Token": { [t('actionGenerateAccessToken')]: "generateAccessToken", [t('actionDeleteAccessToken')]: "deleteAcessToken", @@ -114,6 +110,11 @@ function getActionsCategories(root: boolean) { [t('actionListIdpOrgs')]: "listIdpOrgs", [t('actionUpdateIdpOrg')]: "updateIdpOrg" }; + + actionsByCategory["User"] = { + [t('actionUpdateUser')]: "updateUser", + [t('actionGetUser')]: "getUser" + }; } return actionsByCategory; diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx index 6e1e884f..15fe46d7 100644 --- a/src/components/ProfileIcon.tsx +++ b/src/components/ProfileIcon.tsx @@ -20,7 +20,8 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { useUserContext } from "@app/hooks/useUserContext"; import Disable2FaForm from "./Disable2FaForm"; -import Enable2FaForm from "./Enable2FaForm"; +import SecurityKeyForm from "./SecurityKeyForm"; +import Enable2FaDialog from "./Enable2FaDialog"; import SupporterStatus from "./SupporterStatus"; import { UserType } from "@server/types/UserTypes"; import LocaleSwitcher from "@app/components/LocaleSwitcher"; @@ -39,6 +40,7 @@ export default function ProfileIcon() { const [openEnable2fa, setOpenEnable2fa] = useState(false); const [openDisable2fa, setOpenDisable2fa] = useState(false); + const [openSecurityKey, setOpenSecurityKey] = useState(false); const t = useTranslations(); @@ -70,8 +72,12 @@ export default function ProfileIcon() { return ( <> - + + @@ -105,6 +111,31 @@ export default function ProfileIcon() { )} + {user?.type === UserType.Internal && ( + <> + {!user.twoFactorEnabled && ( + setOpenEnable2fa(true)} + > + {t("otpEnable")} + + )} + {user.twoFactorEnabled && ( + setOpenDisable2fa(true)} + > + {t("otpDisable")} + + )} + setOpenSecurityKey(true)} + > + {t("securityKeyManage")} + + + + )} + {user?.type === UserType.Internal && ( <> {!user.twoFactorEnabled && ( diff --git a/src/components/SecurityKeyForm.tsx b/src/components/SecurityKeyForm.tsx new file mode 100644 index 00000000..de029b10 --- /dev/null +++ b/src/components/SecurityKeyForm.tsx @@ -0,0 +1,859 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useTranslations } from "next-intl"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { createApiClient } from "@app/lib/api"; +import { formatAxiosError } from "@app/lib/api"; +import { toast } from "@app/hooks/useToast"; +import { Button } from "@app/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@app/components/ui/form"; +import { Input } from "@app/components/ui/input"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import { startRegistration } from "@simplewebauthn/browser"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { Card, CardContent } from "@app/components/ui/card"; +import { Badge } from "@app/components/ui/badge"; +import { Loader2, KeyRound, Trash2, Plus, Shield, Info } from "lucide-react"; +import { cn } from "@app/lib/cn"; + +type SecurityKeyFormProps = { + open: boolean; + setOpen: (open: boolean) => void; +}; + +type SecurityKey = { + credentialId: string; + name: string; + lastUsed: string; +}; + +type DeleteSecurityKeyData = { + credentialId: string; + name: string; +}; + +type RegisterFormValues = { + name: string; + password: string; + code?: string; +}; + +type DeleteFormValues = { + password: string; + code?: string; +}; + +type FieldProps = { + field: { + value: string; + onChange: (event: React.ChangeEvent) => void; + onBlur: () => void; + name: string; + ref: React.Ref; + }; +}; + +export default function SecurityKeyForm({ + open, + setOpen +}: SecurityKeyFormProps) { + const t = useTranslations(); + const { env } = useEnvContext(); + const api = createApiClient({ env }); + const [securityKeys, setSecurityKeys] = useState([]); + const [isRegistering, setIsRegistering] = useState(false); + const [dialogState, setDialogState] = useState< + "list" | "register" | "register2fa" | "delete" | "delete2fa" + >("list"); + const [selectedSecurityKey, setSelectedSecurityKey] = + useState(null); + const [deleteInProgress, setDeleteInProgress] = useState(false); + const [pendingDeleteCredentialId, setPendingDeleteCredentialId] = useState< + string | null + >(null); + const [pendingDeletePassword, setPendingDeletePassword] = useState< + string | null + >(null); + const [pendingRegisterData, setPendingRegisterData] = useState<{ + name: string; + password: string; + } | null>(null); + const [register2FAForm, setRegister2FAForm] = useState<{ code: string }>({ + code: "" + }); + + useEffect(() => { + loadSecurityKeys(); + }, []); + + const registerSchema = z.object({ + name: z.string().min(1, { message: t("securityKeyNameRequired") }), + password: z.string().min(1, { message: t("passwordRequired") }), + code: z.string().optional() + }); + + const deleteSchema = z.object({ + password: z.string().min(1, { message: t("passwordRequired") }), + code: z.string().optional() + }); + + const registerForm = useForm({ + resolver: zodResolver(registerSchema), + defaultValues: { + name: "", + password: "", + code: "" + } + }); + + const deleteForm = useForm({ + resolver: zodResolver(deleteSchema), + defaultValues: { + password: "", + code: "" + } + }); + + const loadSecurityKeys = async () => { + try { + const response = await api.get("/auth/security-key/list"); + setSecurityKeys(response.data.data); + } catch (error) { + toast({ + variant: "destructive", + description: formatAxiosError(error, t("securityKeyLoadError")) + }); + } + }; + + const handleRegisterSecurityKey = async (values: RegisterFormValues) => { + try { + // Check browser compatibility first + if (!window.PublicKeyCredential) { + toast({ + variant: "destructive", + description: t("securityKeyBrowserNotSupported", { + defaultValue: + "Your browser doesn't support security keys. Please use a modern browser like Chrome, Firefox, or Safari." + }) + }); + return; + } + + setIsRegistering(true); + const startRes = await api.post( + "/auth/security-key/register/start", + { + name: values.name, + password: values.password, + code: values.code + } + ); + + // If 2FA is required + if (startRes.status === 202 && startRes.data.data?.codeRequested) { + setPendingRegisterData({ + name: values.name, + password: values.password + }); + setDialogState("register2fa"); + setIsRegistering(false); + return; + } + + const options = startRes.data.data; + + try { + const credential = await startRegistration(options); + + await api.post("/auth/security-key/register/verify", { + credential + }); + + toast({ + description: t("securityKeyRegisterSuccess", { + defaultValue: "Security key registered successfully" + }) + }); + + registerForm.reset(); + setDialogState("list"); + await loadSecurityKeys(); + } catch (error: any) { + if (error.name === "NotAllowedError") { + if (error.message.includes("denied permission")) { + toast({ + variant: "destructive", + description: t("securityKeyPermissionDenied", { + defaultValue: + "Please allow access to your security key to continue registration." + }) + }); + } else { + toast({ + variant: "destructive", + description: t("securityKeyRemovedTooQuickly", { + defaultValue: + "Please keep your security key connected until the registration process completes." + }) + }); + } + } else if (error.name === "NotSupportedError") { + toast({ + variant: "destructive", + description: t("securityKeyNotSupported", { + defaultValue: + "Your security key may not be compatible. Please try a different security key." + }) + }); + } else { + toast({ + variant: "destructive", + description: t("securityKeyUnknownError", { + defaultValue: + "There was a problem registering your security key. Please try again." + }) + }); + } + throw error; // Re-throw to be caught by outer catch + } + } catch (error) { + console.error("Security key registration error:", error); + toast({ + variant: "destructive", + description: formatAxiosError( + error, + t("securityKeyRegisterError", { + defaultValue: "Failed to register security key" + }) + ) + }); + } finally { + setIsRegistering(false); + } + }; + + const handleDeleteSecurityKey = async (values: DeleteFormValues) => { + if (!selectedSecurityKey) return; + + try { + setDeleteInProgress(true); + const encodedCredentialId = encodeURIComponent( + selectedSecurityKey.credentialId + ); + const response = await api.delete( + `/auth/security-key/${encodedCredentialId}`, + { + data: { + password: values.password, + code: values.code + } + } + ); + + // If 2FA is required + if (response.status === 202 && response.data.data.codeRequested) { + setPendingDeleteCredentialId(encodedCredentialId); + setPendingDeletePassword(values.password); + setDialogState("delete2fa"); + return; + } + + toast({ + description: t("securityKeyRemoveSuccess") + }); + + deleteForm.reset(); + setSelectedSecurityKey(null); + setDialogState("list"); + await loadSecurityKeys(); + } catch (error) { + toast({ + variant: "destructive", + description: formatAxiosError( + error, + t("securityKeyRemoveError") + ) + }); + } finally { + setDeleteInProgress(false); + } + }; + + const handle2FASubmit = async (values: DeleteFormValues) => { + if (!pendingDeleteCredentialId || !pendingDeletePassword) return; + + try { + setDeleteInProgress(true); + await api.delete( + `/auth/security-key/${pendingDeleteCredentialId}`, + { + data: { + password: pendingDeletePassword, + code: values.code + } + } + ); + + toast({ + description: t("securityKeyRemoveSuccess") + }); + + deleteForm.reset(); + setSelectedSecurityKey(null); + setDialogState("list"); + setPendingDeleteCredentialId(null); + setPendingDeletePassword(null); + await loadSecurityKeys(); + } catch (error) { + toast({ + variant: "destructive", + description: formatAxiosError( + error, + t("securityKeyRemoveError") + ) + }); + } finally { + setDeleteInProgress(false); + } + }; + + const handleRegister2FASubmit = async (values: { code: string }) => { + if (!pendingRegisterData) return; + + try { + setIsRegistering(true); + const startRes = await api.post( + "/auth/security-key/register/start", + { + name: pendingRegisterData.name, + password: pendingRegisterData.password, + code: values.code + } + ); + + const options = startRes.data.data; + + try { + const credential = await startRegistration(options); + + await api.post("/auth/security-key/register/verify", { + credential + }); + + toast({ + description: t("securityKeyRegisterSuccess", { + defaultValue: "Security key registered successfully" + }) + }); + + registerForm.reset(); + setDialogState("list"); + setPendingRegisterData(null); + setRegister2FAForm({ code: "" }); + await loadSecurityKeys(); + } catch (error: any) { + if (error.name === "NotAllowedError") { + if (error.message.includes("denied permission")) { + toast({ + variant: "destructive", + description: t("securityKeyPermissionDenied", { + defaultValue: + "Please allow access to your security key to continue registration." + }) + }); + } else { + toast({ + variant: "destructive", + description: t("securityKeyRemovedTooQuickly", { + defaultValue: + "Please keep your security key connected until the registration process completes." + }) + }); + } + } else if (error.name === "NotSupportedError") { + toast({ + variant: "destructive", + description: t("securityKeyNotSupported", { + defaultValue: + "Your security key may not be compatible. Please try a different security key." + }) + }); + } else { + toast({ + variant: "destructive", + description: t("securityKeyUnknownError", { + defaultValue: + "There was a problem registering your security key. Please try again." + }) + }); + } + throw error; // Re-throw to be caught by outer catch + } + } catch (error) { + console.error("Security key registration error:", error); + toast({ + variant: "destructive", + description: formatAxiosError( + error, + t("securityKeyRegisterError", { + defaultValue: "Failed to register security key" + }) + ) + }); + setRegister2FAForm({ code: "" }); + } finally { + setIsRegistering(false); + } + }; + + const onOpenChange = (open: boolean) => { + if (open) { + loadSecurityKeys(); + } else { + registerForm.reset(); + deleteForm.reset(); + setSelectedSecurityKey(null); + setDialogState("list"); + setPendingRegisterData(null); + setRegister2FAForm({ code: "" }); + } + setOpen(open); + }; + + return ( + <> + + + {dialogState === "list" && ( + <> + + + {t("securityKeyManage")} + + + {t("securityKeyDescription")} + + + +
+
+

+ {t("securityKeyList")} +

+ +
+ + {securityKeys.length > 0 ? ( +
+ {securityKeys.map((securityKey) => ( + + +
+
+ +
+
+

+ { + securityKey.name + } +

+

+ {t( + "securityKeyLastUsed", + { + date: new Date( + securityKey.lastUsed + ).toLocaleDateString() + } + )} +

+
+
+ +
+
+ ))} +
+ ) : ( +
+ +

+ {t("securityKeyNoKeysRegistered")} +

+

+ {t("securityKeyNoKeysDescription")} +

+
+ )} + + {securityKeys.length === 1 && ( + + + + {t("securityKeyRecommendation")} + + + )} +
+
+ + )} + + {dialogState === "register" && ( + <> + + + {t("securityKeyRegisterTitle")} + + + {t("securityKeyRegisterDescription")} + + + +
+ + ( + + + {t( + "securityKeyNameLabel" + )} + + + + + + + )} + /> + ( + + + {t("password")} + + + + + + + )} + /> + + +
+ + + + + + + + )} + + {dialogState === "register2fa" && ( + <> + + + {t("securityKeyTwoFactorRequired")} + + + {t("securityKeyTwoFactorDescription")} + + + +
+
+ + + setRegister2FAForm({ + code: e.target.value + }) + } + maxLength={6} + disabled={isRegistering} + /> +
+
+
+ + + + + + + + )} + + {dialogState === "delete" && ( + <> + + + {t("securityKeyRemoveTitle")} + + + {t("securityKeyRemoveDescription", { name: selectedSecurityKey!.name! })} + + + +
+ + ( + + + {t("password")} + + + + + + + )} + /> + + +
+ + + + + + + + )} + + {dialogState === "delete2fa" && ( + <> + + + {t("securityKeyTwoFactorRequired")} + + + {t("securityKeyTwoFactorRemoveDescription")} + + + +
+ + ( + + + {t("securityKeyTwoFactorCode")} + + + + + + + )} + /> + + +
+ + + + + + + + )} +
+
+ + ); +} diff --git a/src/components/SwitchInput.tsx b/src/components/SwitchInput.tsx index 3a287d41..a2291c2e 100644 --- a/src/components/SwitchInput.tsx +++ b/src/components/SwitchInput.tsx @@ -6,6 +6,7 @@ interface SwitchComponentProps { id: string; label?: string; description?: string; + checked?: boolean; defaultChecked?: boolean; disabled?: boolean; onCheckedChange: (checked: boolean) => void; @@ -16,6 +17,7 @@ export function SwitchInput({ label, description, disabled, + checked, defaultChecked = false, onCheckedChange }: SwitchComponentProps) { @@ -24,6 +26,7 @@ export function SwitchInput({
void; + onCancel?: () => void; + isDialog?: boolean; + email?: string; + password?: string; + submitButtonText?: string; + cancelButtonText?: string; + showCancelButton?: boolean; + onStepChange?: (step: number) => void; + onLoadingChange?: (loading: boolean) => void; +}; + +const TwoFactorSetupForm = forwardRef< + { handleSubmit: () => void }, + TwoFactorSetupFormProps +>( + ( + { + onComplete, + onCancel, + isDialog = false, + email, + password: initialPassword, + submitButtonText, + cancelButtonText, + showCancelButton = false, + onStepChange, + onLoadingChange + }, + ref + ) => { + const [step, setStep] = useState(1); + const [secretKey, setSecretKey] = useState(""); + const [secretUri, setSecretUri] = useState(""); + const [loading, setLoading] = useState(false); + const [backupCodes, setBackupCodes] = useState([]); + + const api = createApiClient(useEnvContext()); + const t = useTranslations(); + + // Notify parent of step and loading changes + useEffect(() => { + onStepChange?.(step); + }, [step, onStepChange]); + + useEffect(() => { + onLoadingChange?.(loading); + }, [loading, onLoadingChange]); + + const enableSchema = z.object({ + password: z.string().min(1, { message: t("passwordRequired") }) + }); + + const confirmSchema = z.object({ + code: z.string().length(6, { message: t("pincodeInvalid") }) + }); + + const enableForm = useForm>({ + resolver: zodResolver(enableSchema), + defaultValues: { + password: initialPassword || "" + } + }); + + const confirmForm = useForm>({ + resolver: zodResolver(confirmSchema), + defaultValues: { + code: "" + } + }); + + const request2fa = async (values: z.infer) => { + setLoading(true); + + const endpoint = `/auth/2fa/request`; + const payload = { email, password: values.password }; + + const res = await api + .post< + AxiosResponse + >(endpoint, payload) + .catch((e) => { + toast({ + title: t("otpErrorEnable"), + description: formatAxiosError( + e, + t("otpErrorEnableDescription") + ), + variant: "destructive" + }); + }); + + if (res && res.data.data.secret) { + setSecretKey(res.data.data.secret); + setSecretUri(res.data.data.uri); + setStep(2); + } + + setLoading(false); + }; + + const confirm2fa = async (values: z.infer) => { + setLoading(true); + + const endpoint = `/auth/2fa/enable`; + const payload = { + email, + password: enableForm.getValues().password, + code: values.code + }; + + const res = await api + .post>(endpoint, payload) + .catch((e) => { + toast({ + title: t("otpErrorEnable"), + description: formatAxiosError( + e, + t("otpErrorEnableDescription") + ), + variant: "destructive" + }); + }); + + if (res && res.data.data.valid) { + setBackupCodes(res.data.data.backupCodes || []); + await api + .post>("/auth/login", { + email, + password: enableForm.getValues().password, + code: values.code + }) + .catch((e) => { + console.error(e); + }); + setStep(3); + } + + setLoading(false); + }; + + const handleSubmit = () => { + if (step === 1) { + enableForm.handleSubmit(request2fa)(); + } else if (step === 2) { + confirmForm.handleSubmit(confirm2fa)(); + } + }; + + const handleComplete = (email: string, password: string) => { + if (onComplete) { + onComplete(email, password); + } + }; + + useImperativeHandle(ref, () => ({ + handleSubmit + })); + + return ( +
+ {step === 1 && ( +
+ +
+ ( + + + {t("password")} + + + + + + + )} + /> +
+
+ + )} + + {step === 2 && ( +
+

{t("otpSetupScanQr")}

+
+ +
+
+ +
+ +
+ + ( + + + {t("otpSetupSecretCode")} + + + + + + + )} + /> + + +
+ )} + + {step === 3 && ( +
+ +

+ {t("otpSetupSuccess")} +

+

{t("otpSetupSuccessStoreBackupCodes")}

+ + {backupCodes.length > 0 && ( +
+ +
+ )} +
+ )} + + {/* Action buttons - only show when not in dialog */} + {!isDialog && ( +
+ {showCancelButton && onCancel && ( + + )} + {(step === 1 || step === 2) && ( + + )} + {step === 3 && ( + + )} +
+ )} +
+ ); + } +); + +export default TwoFactorSetupForm; diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 305d66d3..9871a199 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,4 +1,4 @@ export type Locale = (typeof locales)[number]; -export const locales = ['en-US', 'es-ES', 'fr-FR', 'de-DE', 'nl-NL', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN'] as const; +export const locales = ['en-US', 'es-ES', 'fr-FR', 'de-DE', 'nl-NL', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN', 'ko-KR'] as const; export const defaultLocale: Locale = 'en-US'; \ No newline at end of file