From 37f047ebece5ad9c555896c7cd63ff9201cd77fe Mon Sep 17 00:00:00 2001 From: maestrix Date: Fri, 15 May 2026 22:50:33 +0200 Subject: [PATCH 1/5] feat: first refacto --- .gitignore | 53 ++- .vscode/extensions.json | 3 + .vscode/launch.json | 39 ++ .vscode/settings.json | 11 + README.md | 347 +++--------------- dev-app-update.yml | 3 - electron-builder.yml | 48 --- electron.vite.config.ts | 37 +- package.json | 28 +- resources/entitlements.mac.plist | 12 + resources/icon.icns | Bin 0 -> 85649 bytes resources/icon.ico | Bin 0 -> 20908 bytes resources/icon.png | Bin 35949 -> 65568 bytes src/main/config/{env.ts => api.ts} | 2 +- src/main/icon.ts | 63 ++++ src/main/index.ts | 12 +- src/main/services/auth.ts | 13 +- src/main/services/downloader.ts | 4 +- src/main/services/game.ts | 2 +- src/main/services/gameStatus.ts | 16 +- src/main/services/version.ts | 28 +- src/preload/index.d.ts | 88 +---- src/preload/index.ts | 6 +- src/renderer/{src/app.tsx => App.tsx} | 6 +- .../{src => }/assets/fonts/Poppins-Bold.ttf | Bin .../assets/fonts/Poppins-BoldItalic.ttf | Bin .../{src => }/assets/fonts/Poppins-Italic.ttf | Bin .../assets/fonts/Poppins-Regular.ttf | Bin .../{src => }/assets/icons/dyingstar-logo.png | Bin src/renderer/{src => }/assets/main.css | 0 .../layout/{navbar.tsx => Navbar.tsx} | 12 +- .../layout/{shell.tsx => Shell.tsx} | 19 +- .../{accountPanel.tsx => AccountPanel.tsx} | 10 +- .../panel/{filesPanel.tsx => FilesPanel.tsx} | 22 +- .../panel/{gamePanel.tsx => GamePanel.tsx} | 20 +- .../{socialPanel.tsx => SocialPanel.tsx} | 10 +- .../PanelMessage.tsx} | 0 .../ServerStatus.tsx} | 2 +- .../UpdateAlert.tsx} | 0 .../{loreArticle.tsx => lore/LoreArticle.tsx} | 6 +- .../{loreSidebar.tsx => lore/LoreSidebar.tsx} | 2 +- .../ChangelogModal.tsx} | 0 .../ui/{button.tsx => primitives/Button.tsx} | 0 .../InputField.tsx} | 0 .../icons/DiscordIcon.tsx} | 0 .../{friendItem.tsx => social/FriendItem.tsx} | 2 +- .../{friendRow.tsx => social/FriendRow.tsx} | 0 .../ui/{orgaRow.tsx => social/OrgaRow.tsx} | 2 +- .../{requestRow.tsx => social/RequestRow.tsx} | 2 +- .../SocialSidebar.tsx} | 0 .../{src => }/content/lore/origins.md | 0 src/renderer/env.d.ts | 1 + .../{src => }/hooks/useFitWindowToContent.ts | 0 src/renderer/{src/i18n.ts => i18n/index.ts} | 0 src/renderer/index.html | 2 +- .../{src => }/lib/formatReleaseDate.ts | 0 .../{src => }/lib/isGameUpdateAvailable.ts | 0 src/renderer/{src => }/main.tsx | 2 +- src/renderer/src/assets/electron.svg | 10 - src/renderer/src/assets/wavy-lines.svg | 25 -- src/renderer/src/components/Versions.tsx | 15 - src/renderer/src/env.d.ts | 29 -- src/renderer/{store => stores}/account.ts | 0 .../{store => stores}/availability.ts | 0 src/renderer/{store => stores}/env.ts | 3 +- src/renderer/{store => stores}/files.ts | 0 src/renderer/{store => stores}/game.ts | 2 +- src/renderer/{store => stores}/lore.ts | 6 +- src/renderer/{store => stores}/navigation.ts | 0 src/renderer/{store => stores}/social.ts | 0 src/renderer/{store => stores}/version.ts | 8 +- src/renderer/views/{lore.tsx => Lore.tsx} | 4 +- src/renderer/views/{social.tsx => Social.tsx} | 16 +- src/renderer/views/UniverseView.tsx | 58 +++ src/renderer/views/universe.tsx | 41 --- src/renderer/views/universeTesting.tsx | 51 --- src/{main/vite-env.d.ts => shared/env.d.ts} | 20 +- src/shared/types/auth.ts | 5 + src/shared/types/env.ts | 2 + src/shared/types/game.ts | 14 + src/shared/types/index.ts | 5 + src/shared/types/install.ts | 4 + src/shared/types/version.ts | 14 + tsconfig.json | 6 +- tsconfig.node.json | 15 +- tsconfig.web.json | 16 +- 86 files changed, 491 insertions(+), 813 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json delete mode 100644 dev-app-update.yml delete mode 100644 electron-builder.yml create mode 100644 resources/entitlements.mac.plist create mode 100644 resources/icon.icns create mode 100644 resources/icon.ico rename src/main/config/{env.ts => api.ts} (98%) create mode 100644 src/main/icon.ts rename src/renderer/{src/app.tsx => App.tsx} (73%) rename src/renderer/{src => }/assets/fonts/Poppins-Bold.ttf (100%) rename src/renderer/{src => }/assets/fonts/Poppins-BoldItalic.ttf (100%) rename src/renderer/{src => }/assets/fonts/Poppins-Italic.ttf (100%) rename src/renderer/{src => }/assets/fonts/Poppins-Regular.ttf (100%) rename src/renderer/{src => }/assets/icons/dyingstar-logo.png (100%) rename src/renderer/{src => }/assets/main.css (100%) rename src/renderer/components/layout/{navbar.tsx => Navbar.tsx} (98%) rename src/renderer/components/layout/{shell.tsx => Shell.tsx} (51%) rename src/renderer/components/panel/{accountPanel.tsx => AccountPanel.tsx} (90%) rename src/renderer/components/panel/{filesPanel.tsx => FilesPanel.tsx} (92%) rename src/renderer/components/panel/{gamePanel.tsx => GamePanel.tsx} (89%) rename src/renderer/components/panel/{socialPanel.tsx => SocialPanel.tsx} (89%) rename src/renderer/components/ui/{panelMessage.tsx => feedback/PanelMessage.tsx} (100%) rename src/renderer/components/ui/{serverStatus.tsx => feedback/ServerStatus.tsx} (94%) rename src/renderer/components/ui/{updateAlert.tsx => feedback/UpdateAlert.tsx} (100%) rename src/renderer/components/ui/{loreArticle.tsx => lore/LoreArticle.tsx} (90%) rename src/renderer/components/ui/{loreSidebar.tsx => lore/LoreSidebar.tsx} (96%) rename src/renderer/components/ui/{changelogModal.tsx => overlays/ChangelogModal.tsx} (100%) rename src/renderer/components/ui/{button.tsx => primitives/Button.tsx} (100%) rename src/renderer/components/ui/{inputField.tsx => primitives/InputField.tsx} (100%) rename src/renderer/components/ui/{discordIcon.tsx => primitives/icons/DiscordIcon.tsx} (100%) rename src/renderer/components/ui/{friendItem.tsx => social/FriendItem.tsx} (97%) rename src/renderer/components/ui/{friendRow.tsx => social/FriendRow.tsx} (100%) rename src/renderer/components/ui/{orgaRow.tsx => social/OrgaRow.tsx} (99%) rename src/renderer/components/ui/{requestRow.tsx => social/RequestRow.tsx} (98%) rename src/renderer/components/ui/{socialSidebar.tsx => social/SocialSidebar.tsx} (100%) rename src/renderer/{src => }/content/lore/origins.md (100%) create mode 100644 src/renderer/env.d.ts rename src/renderer/{src => }/hooks/useFitWindowToContent.ts (100%) rename src/renderer/{src/i18n.ts => i18n/index.ts} (100%) rename src/renderer/{src => }/lib/formatReleaseDate.ts (100%) rename src/renderer/{src => }/lib/isGameUpdateAvailable.ts (100%) rename src/renderer/{src => }/main.tsx (90%) delete mode 100644 src/renderer/src/assets/electron.svg delete mode 100644 src/renderer/src/assets/wavy-lines.svg delete mode 100644 src/renderer/src/components/Versions.tsx delete mode 100644 src/renderer/src/env.d.ts rename src/renderer/{store => stores}/account.ts (100%) rename src/renderer/{store => stores}/availability.ts (100%) rename src/renderer/{store => stores}/env.ts (72%) rename src/renderer/{store => stores}/files.ts (100%) rename src/renderer/{store => stores}/game.ts (97%) rename src/renderer/{store => stores}/lore.ts (65%) rename src/renderer/{store => stores}/navigation.ts (100%) rename src/renderer/{store => stores}/social.ts (100%) rename src/renderer/{store => stores}/version.ts (86%) rename src/renderer/views/{lore.tsx => Lore.tsx} (59%) rename src/renderer/views/{social.tsx => Social.tsx} (95%) create mode 100644 src/renderer/views/UniverseView.tsx delete mode 100644 src/renderer/views/universe.tsx delete mode 100644 src/renderer/views/universeTesting.tsx rename src/{main/vite-env.d.ts => shared/env.d.ts} (52%) create mode 100644 src/shared/types/auth.ts create mode 100644 src/shared/types/env.ts create mode 100644 src/shared/types/game.ts create mode 100644 src/shared/types/index.ts create mode 100644 src/shared/types/install.ts create mode 100644 src/shared/types/version.ts diff --git a/.gitignore b/.gitignore index 6e48aa9..d31d55c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,44 +1,35 @@ # Dependencies -node_modules -npm-debug.log* -yarn-debug.log* -yarn-error.log* +node_modules/ +.pnpm-store/ + +# Logs & cache +*.log +logs/ .eslintcache -# Next.js -.next/ -out +# Environment (secrets — ne jamais committer) +.env +.env.local +.env.*.local + +# Builds Electron / Vite +out/ +dist/ +release/ build/ -# Production builds -dist +# Archives *.tgz -# Environment variables -.env.local -.env.development.local -.env.test.local -.env.production.local - -# Logs -*.log* -logs/ +# OS +.DS_Store +Thumbs.db +ehthumbs.db -# Editor directories and files -.vscode/ +# Editors (préférences personnelles uniquement) .idea/ *.swp *.swo -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Electron +# Electron legacy app/ -release/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..940260d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0b6b9a6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,39 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Main Process", + "type": "node", + "request": "launch", + "cwd": "${workspaceRoot}", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", + "windows": { + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" + }, + "runtimeArgs": ["--sourcemap"], + "env": { + "REMOTE_DEBUGGING_PORT": "9222" + } + }, + { + "name": "Debug Renderer Process", + "port": 9222, + "request": "attach", + "type": "chrome", + "webRoot": "${workspaceFolder}/src/renderer", + "timeout": 60000, + "presentation": { + "hidden": true + } + } + ], + "compounds": [ + { + "name": "Debug All", + "configurations": ["Debug Main Process", "Debug Renderer Process"], + "presentation": { + "order": 1 + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4c05394 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/README.md b/README.md index 402541e..c1fd0dc 100644 --- a/README.md +++ b/README.md @@ -1,323 +1,72 @@ -# 🌟 Dying Star Launcher +# Dying Star Launcher -Un lanceur de jeu moderne et élégant construit avec Next.js et Electron. +Launcher desktop open source pour le jeu **Dying Star**, basé sur [Electron](https://www.electronjs.org/) + [electron-vite](https://electron-vite.org/), avec une interface **React 19** et **Tailwind CSS v4**. -## 🛠️ Technologies utilisées +## Stack -- **Next.js 14** - Framework React pour l'interface utilisateur -- **Electron 38** - Framework pour créer des applications desktop -- **React 18** - Bibliothèque JavaScript pour l'UI -- **Node.js** - Environnement d'exécution JavaScript -- **Nodemon** - Rechargement automatique en développement -- **Concurrently** - Exécution simultanée de processus -- **dotenv** - Gestion des variables d'environnement +| Couche | Technologie | +|--------|-------------| +| Shell desktop | Electron 38 | +| Build | electron-vite, Vite 7 | +| UI | React 19, Tailwind CSS v4 | +| État | Zustand | +| i18n | i18next / react-i18next | +| Packaging | electron-builder | -## 📦 Installation +## Structure du projet -### Prérequis -- **Node.js** (version 18 ou supérieure) -- **npm** ou **yarn** - -### Installation automatique - -```bash -# Clonez le repository -git clone https://github.com/DyingStar-game/launcher.git -cd launcher - -# Installation complète avec configuration -npm run setup ``` - -### Installation manuelle - -```bash -# 1. Clonez le repository -git clone https://github.com/DyingStar-game/launcher.git -cd launcher - -# 2. Installez les dépendances -npm install - -# 3. Copiez la configuration d'environnement -cp .env.example .env.local - -# 4. Lancez en mode développement -npm run electron-dev +src/ +├── main/ # Process principal Electron (IPC, auth, téléchargement, jeu) +├── preload/ # Pont sécurisé contextBridge → window.api +├── renderer/ # Application React (composants, vues, stores, i18n) +└── shared/ # Types TypeScript partagés (main, preload, renderer) +resources/ # Icônes et entitlements macOS (versionnés) ``` -## 🎮 Scripts disponibles - -### Développement -- `npm run dev` - Lance Next.js en mode développement -- `npm run electron-dev` - 🚀 **Recommandé** - Lance Next.js + Electron (logs propres) -- `npm run electron-dev-verbose` - Lance avec logs détaillés de nodemon -- `npm run electron` - Lance Electron seul - -### Production -- `npm run build` - Build l'application Next.js -- `npm run electron-pack` - Build l'application Electron pour la production -- `npm run export` - Export statique Next.js - -### Utilitaires -- `npm run clean` - Nettoie les dossiers de build -- `npm run setup` - Installation complète automatique - -## ⚙️ Configuration environnement - -Le projet utilise des variables d'environnement pour une configuration flexible. - -### Fichiers de configuration +Alias TypeScript / Vite : `@shared`, `@components`, `@views`, `@stores`, `@hooks`, `@lib`, `@i18n`, `@content`, `@assets`. -- **`.env.example`** - Template avec toutes les variables disponibles -- **`.env.local`** - Configuration locale (ignorée par git) - -### Variables principales - -```bash -# Mode de développement -NODE_ENV=development - -# Configuration Electron -ELECTRON_DISABLE_SECURITY_WARNINGS=true -WINDOW_WIDTH=1200 -WINDOW_HEIGHT=800 -ENABLE_DEVTOOLS=true - -# Configuration Next.js -NEXT_DEV_URL=http://localhost:3000 - -# Debug -DEBUG_ELECTRON=false -NODEMON_VERBOSE=false -``` +## Prérequis -### Personnalisation +- Node.js 20+ +- npm ou pnpm -Modifiez `.env.local` pour adapter le launcher à vos préférences : +## Installation ```bash -# Fenêtre plus grande -WINDOW_WIDTH=1600 -WINDOW_HEIGHT=1000 - -# Désactiver les DevTools -ENABLE_DEVTOOLS=false - -# Activer le debug complet -DEBUG_ELECTRON=true -NODEMON_VERBOSE=true -``` - -## 📁 Structure du projet - -``` -dying-star-launcher/ -├── electron/ # Fichiers Electron -│ ├── main.js # Point d'entrée Electron (avec config env) -│ └── preload.js # Script de préchargement (optionnel) -├── pages/ # Pages Next.js -│ ├── index.js # Page d'accueil -│ └── _app.js # App wrapper -├── public/ # Ressources statiques -├── styles/ # Fichiers CSS -├── .env.example # Template de configuration -├── .env.local # Configuration locale (à créer) -├── nodemon.json # Configuration nodemon -├── package.json # Configuration npm -├── next.config.js # Configuration Next.js -└── README.md # Documentation -``` - -## 🔧 Développement - -### Mode développement recommandé - -```bash -npm run electron-dev -``` - -Cette commande lance simultanément : -- 🌐 **Next.js** sur `http://localhost:3000` avec hot-reload -- ⚡ **Electron** avec rechargement automatique via nodemon - -### Logs propres - -Les logs sont maintenant formatés avec des préfixes colorés : -``` -🌐 Next.js | ✓ Ready in 1141ms -⚡ Electron | ✅ Electron window ready -⚡ Electron | [nodemon] restarting due to changes... -``` - -### Debug avancé - -Pour plus de détails pendant le développement : - -```bash -# Mode verbose avec tous les logs -npm run electron-dev-verbose - -# Ou modifiez .env.local -DEBUG_ELECTRON=true -NODEMON_VERBOSE=true -``` - -### Build pour production - -```bash -# Build complet -npm run preelectron-pack -npm run electron-pack - -# Ou séparément -npm run build -npm run electron-pack -``` - -Les exécutables seront générés dans le dossier `dist/`. - -## 🎯 Fonctionnalités - -### ✅ Implémenté -- ✅ Interface Next.js moderne -- ✅ Application Electron native -- ✅ Hot-reload en développement -- ✅ Configuration par variables d'environnement -- ✅ Logs propres et colorés -- ✅ Rechargement automatique avec nodemon -- ✅ Build de production -- ✅ Gestion des erreurs - -### 🚧 En développement -- 🚧 Interface utilisateur du launcher -- 🚧 Gestion des jeux installés -- 🚧 Système de mise à jour -- 🚧 Thèmes personnalisables - -### 📋 Roadmap -- [ ] Intégration Steam/Epic Games -- [ ] Chat intégré -- [ ] Store de mods -- [ ] Système d'achievements -- [ ] Profils utilisateur - -## 🛠️ Problèmes courants - -### Erreurs Electron - -Si vous rencontrez des erreurs `Autofill.enable` : -```bash -# Dans .env.local -ELECTRON_DISABLE_SECURITY_WARNINGS=true -``` - -### Port déjà utilisé - -Si le port 3000 est occupé : -```bash -# Dans .env.local -NEXT_DEV_URL=http://localhost:3001 -``` - -Puis lancez Next.js sur le nouveau port : -```bash -npx next dev -p 3001 -``` - -### Problèmes de performance - -Pour optimiser les performances : -```bash -# Dans .env.local -ENABLE_DEVTOOLS=false -DEBUG_ELECTRON=false -``` - -## 🤝 Contribution - -Les contributions sont les bienvenues ! Voici comment procéder : - -### Setup développeur - -```bash -# Fork et clone -git clone https://github.com/VOTRE-USERNAME/launcher.git +git clone https://github.com/DyingStar-game/launcher.git cd launcher - -# Installation -npm run setup - -# Créer une branche -git checkout -b feature/ma-fonctionnalite +npm install +cp .env.example .env +# Éditer .env selon l’environnement (API, ZIP de test, etc.) ``` -### Workflow de contribution - -1. **Fork** le projet -2. **Créer** une branche pour votre fonctionnalité (`git checkout -b feature/AmazingFeature`) -3. **Commit** vos changements (`git commit -m 'Add some AmazingFeature'`) -4. **Push** vers la branche (`git push origin feature/AmazingFeature`) -5. **Ouvrir** une Pull Request - -### Standards de code - -- Utilisez les variables d'environnement pour la configuration -- Ajoutez des logs avec les préfixes appropriés -- Testez en mode développement ET production -- Documentez les nouvelles variables d'environnement - -## 📝 License - -Ce projet est sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails. - -## 📞 Support - -Pour toute question ou problème : - -- 🐛 **Bugs** : [Ouvrir une issue](https://github.com/DyingStar-game/launcher/issues) -- 💡 **Suggestions** : [Discussions](https://github.com/DyingStar-game/launcher/discussions) -- 📧 **Contact** : Contactez l'équipe de développement - -## 🏆 Contributeurs - -Merci à tous les contributeurs qui ont participé à ce projet ! +## Scripts ---- +| Commande | Description | +|----------|-------------| +| `npm run dev` | Développement avec rechargement à chaud | +| `npm run build` | Vérification TypeScript + build de production (`out/`) | +| `npm run start` | Prévisualiser le build packagé | +| `npm run typecheck` | Contrôle TypeScript (main + renderer) | +| `npm run lint` | ESLint | +| `npm run format` | Prettier | +| `npm run build:win` | Installateur Windows (NSIS) | +| `npm run build:mac` | Application macOS | +| `npm run build:linux` | AppImage / deb / rpm | -*Développé avec ❤️ par l'équipe DyingStar* +Les artefacts de distribution sont générés dans `release/`. -# README GENERATED BY ELECTRON +## Variables d’environnement -An Electron application with React and TypeScript +Copier `.env.example` vers `.env`. Les variables exposées au code utilisent le préfixe `VITE_` (voir le fichier d’exemple pour les URLs d’API, archives de jeu, navigation, statut serveur, etc.). -## Recommended IDE Setup +## Contribution -- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +1. Créer une branche depuis `develop` +2. `npm run typecheck && npm run lint` avant toute PR +3. Respecter la structure `src/main`, `src/preload`, `src/renderer`, `src/shared` -## Project Setup +## Licence -### Install - -```bash -$ npm install -``` - -### Development - -```bash -$ npm run dev -``` - -### Build - -```bash -# For windows -$ npm run build:win - -# For macOS -$ npm run build:mac - -# For Linux -$ npm run build:linux -``` +Voir le dépôt GitHub du projet pour les conditions de licence. diff --git a/dev-app-update.yml b/dev-app-update.yml deleted file mode 100644 index ee464d9..0000000 --- a/dev-app-update.yml +++ /dev/null @@ -1,3 +0,0 @@ -provider: generic -url: https://example.com/auto-updates -updaterCacheDirName: react-ts-updater diff --git a/electron-builder.yml b/electron-builder.yml deleted file mode 100644 index 9b4abf4..0000000 --- a/electron-builder.yml +++ /dev/null @@ -1,48 +0,0 @@ -appId: com.electron.app -productName: react-ts -protocols: - - name: DyingStar Launcher - schemes: - - dyingstar -directories: - buildResources: build -files: - - '!**/.vscode/*' - - '!src/*' - - '!electron.vite.config.{js,ts,mjs,cjs}' - - '!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' - - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' - - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' -asarUnpack: - - resources/** -win: - executableName: react-ts -nsis: - artifactName: ${name}-${version}-setup.${ext} - shortcutName: ${productName} - uninstallDisplayName: ${productName} - createDesktopShortcut: always -mac: - entitlementsInherit: build/entitlements.mac.plist - extendInfo: - - NSCameraUsageDescription: Application requests access to the device's camera. - - NSMicrophoneUsageDescription: Application requests access to the device's microphone. - - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. - - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. - notarize: false -dmg: - artifactName: ${name}-${version}.${ext} -linux: - target: - - AppImage - - snap - - deb - - rpm - maintainer: electronjs.org - category: Utility -appImage: - artifactName: ${name}-${version}.${ext} -npmRebuild: false -publish: - provider: generic - url: https://example.com/auto-updates diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 274b8df..6b1bbbf 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -3,24 +3,35 @@ import { defineConfig, loadEnv } from 'electron-vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' -export default defineConfig(({mode}) => { - +const sharedAlias = { + '@shared': resolve(__dirname, 'src/shared') +} + +const rendererAlias = { + ...sharedAlias, + '@hooks': resolve(__dirname, 'src/renderer/hooks'), + '@lib': resolve(__dirname, 'src/renderer/lib'), + '@assets': resolve(__dirname, 'src/renderer/assets'), + '@content': resolve(__dirname, 'src/renderer/content'), + '@stores': resolve(__dirname, 'src/renderer/stores'), + '@components': resolve(__dirname, 'src/renderer/components'), + '@views': resolve(__dirname, 'src/renderer/views'), + '@i18n': resolve(__dirname, 'src/renderer/i18n/index.ts') +} + +export default defineConfig(({ mode }) => { const env = loadEnv(mode) Object.assign(process.env, env) return { - main: {}, - preload: {}, + main: { + resolve: { alias: sharedAlias } + }, + preload: { + resolve: { alias: sharedAlias } + }, renderer: { - resolve: { - alias: { - '@renderer': resolve(__dirname, 'src/renderer/src'), - '@store': resolve(__dirname, 'src/renderer/store'), - '@components': resolve(__dirname, 'src/renderer/components'), - '@views': resolve(__dirname, 'src/renderer/views'), - '@i18n': resolve(__dirname, 'src/renderer/i18n') - } - }, + resolve: { alias: rendererAlias }, plugins: [tailwindcss(), react()] } } diff --git a/package.json b/package.json index 4c24bf1..bf6cc68 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "output": "release" }, "files": [ - "dist/**/*", "out/**/*", "!**/*.map" ], @@ -26,6 +25,13 @@ "npmRebuild": true, "nodeGypRebuild": false, "buildDependenciesFromSource": false, + "extraResources": [ + { + "from": "resources/", + "to": ".", + "filter": ["icon.ico", "icon.icns", "icon.png"] + } + ], "win": { "target": [ { @@ -35,7 +41,12 @@ ] } ], - "icon": "build/icon.ico" + "icon": "resources/icon.ico" + }, + "mac": { + "icon": "resources/icon.icns", + "entitlements": "resources/entitlements.mac.plist", + "entitlementsInherit": "resources/entitlements.mac.plist" }, "nsis": { "oneClick": false, @@ -64,6 +75,7 @@ ] } ], + "icon": "resources/icon.png", "category": "Game" } }, @@ -79,28 +91,21 @@ "postinstall": "electron-builder install-app-deps", "build:unpack": "npm run build && electron-builder --dir", "build:win": "npm run build && electron-builder --win", - "build:mac": "electron-vite build && electron-builder --mac", - "build:linux": "electron-vite build && electron-builder --linux" + "build:mac": "npm run build && electron-builder --mac", + "build:linux": "npm run build && electron-builder --linux" }, "dependencies": { "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", "axios": "^1.16.0", - "debug": "^4.4.3", "electron-log": "^5.4.3", - "electron-store": "^11.0.2", - "electron-updater": "^6.3.9", "extract-zip": "^2.0.1", - "get-stream": "^9.0.1", "i18next": "^26.0.8", - "ms": "^2.1.3", - "openid-client": "^6.8.4", "react-i18next": "^17.0.6", "react-markdown": "^10.1.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", - "yauzl": "^3.3.0", "zustand": "^5.0.13" }, "devDependencies": { @@ -113,7 +118,6 @@ "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", - "autoprefixer": "^10.5.0", "electron-builder": "^26.0.12", "electron-vite": "^5.0.0", "eslint": "^9.39.1", diff --git a/resources/entitlements.mac.plist b/resources/entitlements.mac.plist new file mode 100644 index 0000000..38c887b --- /dev/null +++ b/resources/entitlements.mac.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + + diff --git a/resources/icon.icns b/resources/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..28644aa9d97942c50008d03bc0f93505f7824737 GIT binary patch literal 85649 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

-KY5Dg)0#~?~rEbJH_9`n*G*i+1Ec#;TIAa-b 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 literal 0 HcmV?d00001 diff --git a/resources/icon.ico b/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1b93f4c48896dafdd7439c23bdc668a9993c6b6e GIT binary patch literal 20908 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 Y4%Eq_%Ti|t1}%d0ZW?NrUbhea7jKZmU;qFB literal 0 HcmV?d00001 diff --git a/resources/icon.png b/resources/icon.png index cf9e8b2c87b5c18ac0b26913af6fd3ed00ec3bfb..ca3c5a650e29d119b0e3cb75502bceb0c110dbd1 100644 GIT binary patch literal 65568 zcmb5WWmMIR*7gkxkdkgex29Q?T!etqDUyqjZlpVuZX~6oySux)W5v7N=j?O# zeLv3_?|44J2VlTj|2gM%{jNC)(@>Mg!gzs!fPjFds37we0RaI26o7#C4F2Kz%k(1x z0xp80jHLD##N$@<3}e|uap)VjGq1{DQ!59qp2}7<3IMdmDr8N`ezdR1o%o8>{W7NA z)b{y8Lis#=d83hm(Sab856^c zCkc8BHMH29`dX~$JUks$MCs11q!?=6L6*ZGsdn_HD-W^Rj)hM16u}Rh*_MYGZn%hrdLzQK)WN>&hTOJV>X5Nn5-nQO!c7~xo ztfJyaoz~51S~v}20q0lw4!SBJatoje80w82Be8}zP98UDF_U<5HzTlpO75=WD|_;3 zA<>O$82*FV+iOm#oQIAo^VcC~;PBvVnGWOJ393c}x;4UTAbx%}5nVQ6e^ALKqJ(Am zIw9Q`Dr5(D|2+btqBVX51@7wc?zIc+ zu2KPnWc5imHXatO6wp&P=RA7=a%MYh&otJA>9FGHHNLh=o*UU{z58(X@^1D-yqHYB zElU(Y=`Sgbz!jL%Ft$j?#g03!Q@8UdvGsi!Qg=37S>^-0mKZshfZSYdp}r6^Wt#Kh zrutsNqKKlG+5ttP&fZmSzzuKL>JN#Wa3K;x%_~F_3fkz_>v96L5Oge&Vq0afh-ME% zWF&fB&X7Oy`&sKGxB#0CO$1sdwcf(Nl%H~YEUrbHyJ^Ur{i+D{d)yq;#XSeDb5&za z*jwQaZiMK-e)Q$+=l!1$M!&~9SD@{dTD&1jenCeSk~YLJ4N^)()thiWpZOvSS=-Z{ zHdYGS0D~cJg}WIHrur+AYM&-$(fa$o!QZM_TtMp)JwEGKt0O#S0P=T3W{B(Y7r}s$ z@{d9SAQ0rK_HGI}i~lNL=rOiBSZc*v+s-ky#5aC}7vq5)WvB3qcQMAUrovmHL3CPY z!b+{G2!d_%cd+MORWz~ksE2cTpk|$pW+cdFX1IrrPS1h%pjCJja6;z`Dly zZ@P0<0HM+5@NId<&@5_S%*>O`3t>T!$=qdcU@bmH_E_PlK=Hj9o)=2c<6Kl4jQF=Z z&7}nfFH^Uef^IE>M*}&_H!Atb^QA02w9qjbGj}xVGo3`2B4ltT_kDbjck)wu`OFz$ zcgTY+JDNT#o%fKCM$KfBhbD-aiob(jTocu5DEhR=@xIk=L2+NWAO3^`kRnkh)$uk{iaz zm7IF|U@MFq{^D?&0pmNOkUy2911WR<{SgZb^4CW4O|{{k2bS+Tnl#~x!{@C47qQ3( zD{fDhga-EuGAQ$}n$x3Y;Bnjc@x&Xq^K|)~P&`MvM^$$n9jDjFdCVjr&bLphMeh+J z%pWLQf;jtgaXkb}g@CecFQQ(xGE~^UTL;H~vCH0mUF9QpF8Bt}g@}lOH39E)E;!Rv zb0?1VHPGxlq@&6xDWh`YZ|9+)vP2-n7dOq$px+D1aTHo{V<;b#lTob#!c(j7`K($?x>!E@5JZ}KMn zbUCHCmb`VSNy!Cbg}UOMpWnP(_e1{ZmlJTZhNU!!pv^N~8}mBhi9qLik~x}l*x9e^ zN^oKn^Z4pB;ScXmuXLEG{gmHr)jmvS zbe{{ncgVqYqVs-#tL=*B3L2o+0jTU=Wjr%Uaz6CST$V!bFd+`FOWw`D?wLSxqv{vg zlPbh5bB+!QdDv6y5XE|loN+$prb|VUka8unElrW4q9sdo+gjb!_3-kgGRlUxxnpxh zkyCW9m&y+9*+p>sJ{~h8vg<;^@Sh?&X>nLaR6B~oF7r@t=F19{5U&UT?M7T>dy`e* z_J_QyB1FOUZ0;|zcdq%y-`F(MV#X{w@{V-nRoj}0C-o-JpZ{+ zaVfhh8~PdrvL>}%&aRIh$P^#3DPfNBJ&@T$KIriV% zYkU({fg=3B@=lSO1|LDu^!Ms%j_qcNiyV#s6xeTeZ@NIkrcrdzT0MGjVHWMU-TU$n z$IlQMBi5NUGwjX1X?YIYSntP{Avf8lMyrnag=pYplyk#eym7{_##1GXa%5Kwtht43 zAM&i-e;upq+72@7{a!MeN?jp)P;+*d0YAh_nhb@b&on*RtdXq&MVQ`tzplUh_jwQPNqX&}dC^nBb*2J2UOi0id{=05(My?ygE*~8KI2xsNu zp*Yst7Oh=zxL-u5_oQxWp96o!s-tkf3cboK+|`|>C}%5WYCHF-eBR;ED&?n@CRty) zOP$VR717V2Ex;5j`MXUCPaz!Ko6B~x$c+PJx_ZnNzeIa33Q!VBc1HfuUQsa|wF*v} zpPvGti*d|CB~S#bWU|d1Ba9+9R~z&K)<2Eokur1;Mwh5rDOQ-0Lph5c8WaLCdt;7- zWT+^_lctdKmkWzp`idfV2I#6#k&T}A(X0;v=DTdeeT0tKAQU^^ZFKA$c7$U}?~rB@ zTBvsiu45F{-E=~q)84>D(zz1dgevZXy3T{44h4c{VH5T{4{te2e=(XI(xJGe3*0M| z`ZcdS=0{FUeLtiZ*u60ug{Y6nhv(|uVroXPWJ-%ydi5kI88}+M+0^W4iTec{q1NuZ z@Hy}j50oVY7#&`71Fo=4AYtjrGi==P&oT;(U*%k?p)7lz+W(}Ipa~=CwOYs9=L7)e zVwxWol?3K5Xuo8mh`kK+6#>qik1QkAkxbA!=7>G39Pf0%Mzp#;duKF-YcZ@Nn#~b@ zvCOp9M?YV{+HO6zq_%tU%o*zriv%1dpEHj3ks$Jw9sAH4@NRYgcQB4@?t9Ab+f$pf zwZWsv+X^eDkDgOjtbfKwBQDx}QYqq_uRUUtIeQ&xF})omC&A6_W1N-g6VvZ zQwlN?dNjkj(<+L|=|QYKsIdbShlIAHRKM&>=3f*;9XxVg{#rSlHF9s?9eV>nk?imp zJCqZf@H$+5S$O4wvP(C+IhA**$*g82?}6*b^QClZg=R8zV@HkabmzrBTMzbA_#3fze5Vf*uU=M&n5I%v3Rp8$b|D#ClW5!WL85?uZ%5NuA{1P7 zKT?-ZT{liZ+mqh8!nm^2G49Y5mw!4O217dIHG%;5C z#f2WHUr%5qgU*yN854*64v4>htENmF%{a*7KK$8)c(aNB5S^VYq3v|jI9&E6%_x<#RFO@)vhYCqx#I5EqjkPc*jpjb^|XI?0*OA*QRbVecdTrRAINP?k-#V{;x-YQ4u}qtgG+yyZXDgpeNDt_Kzjc(LQZCDf({pBCC!Tb0wD^JYW2mS3``xHQ=rG;5 zI`XL6|&6vC$9PLPcBfspW!eg^FoB8S29c<|mwQnitJRu2)vf@$C4v}rX(fwu* zz5T{105fbZ*ikMn=fLbjpP_QKz}8|oQ*>MS#8S;Gdkhe}QV%Wig06jgsT5gj0FD#- zdE02x#Z9}0qDe8BydgSDvP{+imKRGD7QgFcw8A5g=D&4wMx)T@Wqm%GT-?uq(Wnkt zp4kq_OZ`?UuI%us6?5K2z;Y}Zq7x%AhBq!tpyYBlNAV4)e~075_#T;Ht>c+k`frcA zg8om)Lf48(H?}+d$(Ek%TgtvjL68~7@u0j5vKL!#bhj1+1gL7c)1+k9u&+D~W0T>w z0-WxuxWLQ2()hlheV(Bz)QOYP<@;xz+iah-*jflOQMiIW>gQL_YNtLKcKj!Fv zUCs~8odi9!q@B6yJqtEBN;O*8rC|yY&vdc)Y|)o$cM@s<#Gd*>t6%|Zklv{fy`0#K<6B=*QWCHhi~_I^@6-R1_p>Pt1GW%wy5rNNT;YjPrrVEYss`=5A@Uj; zI6et(KgF9T0jxwl!oTZ}dy>n~AB%VfMA()t5kl|S(3p12PBF*yz`+lDpC+U7Hwz%w z7p(Pd4>?4_fY8LW_d>*r4tIL@6E&KM@j;w~5Gq!LJLHG!lIyZvV4=E=7cH+1WQw8I zAS;=STyN;0{K#VVQCGOlE}bAt>i27fMAmopv}F+X4;G#_zf{&*n#|-}l*x_^Ub{dd zQpw1n8L1QCzUxnwqLM?sFbn=hPK*E%yE4JRe3M0{N>fRwrmVP}ZRO@g!+e(g-bGjU zoqm>sg$W-NoAU)n#|K)$5p*;nZ-GIJiUL~4tgBrAwQyoY;~=i|;x}9vQ`rQ83*Nn` zRXkxg5wvN0Y>mMu2Xf?lXPrg6$*EMg#KWuh_eYfeZ)nzE{CriKg{w1@{b7iQwR?|6K0uqrf02K(B_7Y-CnAkeIh~5u*eS>R6lqEda&@;?%WlVRe~J` zB6q#uP00Pbx?K}DVf6dA7g!l;`@QoU&);tq?SiNq)rr~x*ZRx`0|H}Q6wLXoTl!3e zGN91#jvsjE?i1mUwbxK|trexzuH1OKAJ|G8*+*q!Ld@KIVWH{xE}i1vOdz$kBAiat z&Nj;yX9~&o-g39RMduh20qW+BRl4IEL%n5xxW?7iYO3Htt;Yc`-`@K!k-aLq&sX{m z2A24;KQ$ZOxotlf&(^e!4y~X=bV{5H@fwAXZ310ysRA7#JE>E%K%(J~O=1}fH9~II zL!4jfjM5ffM-(+nw-reZ`GZxLfFI)R za?mmA!rRCL0aVm%EM)3$q zNI0+dF`)FewCXe75@R{iyaHlczH_VR{H!G z`rd$SzS8Ba$t(IUHsz%k!#6&2EaXFqgDt_lM<4%IcD{>G0#rWK ziQ!^4JR9y^%n!l`g6lt`EFOq%nD$;AwKM1X z>X-Y*7F;y2jCWwyB~(?7Tr>zV2joP_La>Hlbf>T|&fUJ8OO!N(HiGX?RLzs^ite^w z1Y^y5whX6F>xmp9sHrDCk*_1-VSw&ir(w82nV6%6czFQ4eA>nBY>^6JUHZxa<-J zo|(h_hyOsf&*e=m8c2hfBkE!v-0j>e)=hRNK@GxgLF+_*5OY2=vwgS=necj{ z7Rqx=;ODL4hskoUUO+91=9k{S$v}x^_f}$=$wOUo{qkF;4Vrct8 zytWJNvt??x?0P@4g&j;wtymqWMK*V*QJh+the_9+EXzhdsF{0ojz)tVj}YbN*jPbS#*u<@&n1 zj0L=)2K>GNa;jKaO<9<|&Cu95QT8q14D(Lgl$TgBBHF`oXV<`^eEIcJ*t#6{yVt9Y z)u@oi^=0I|^c8yJsD}t)y3EH>B5-5?Oq}SJV1sjROf_@%@@6_R3029;-dA(C{DSfk zd3fpwO(-HJqt?Uaar-7Fm^aNqZRF!aNVM&_<~whHjNi0vs4=YTgot;tk|bKaw!oI| z{9MHyjh2XTkwkYd8>P!D=a1q=Ui$VyXL|&mmd9*A=!%LlBU9B=#CxA@7MRU@;}7D7 z>Qp5yCkN|$xh-VW-&Am1dB^;0k7zSJ#>w6Agmsq;GS5G@31vR0b9)kyUwYWK^i<2W zH}`zm_Rx9qVdL}Zy%^5ntK~vz;@4SuOyH{~r>YnmnPcNdW~Br+a8D10)Z31Ln&?O} zm~=V0#%)qI#*iPwbM6>*3toQ2KqNz@gV_SS?Ap4s8X#HC2KVLbXjNl`%UW7lf2>(Q z0ObW{)`0!NbG3jYv39P`j_dM?Jx_$M9{83YQ~So|Lvuprsq!+8>f1#Y1glmT z-4^$*L-B-!{)XDS8Q{2^1H{mEW;;Gt|9q1*aq@Vry0L&}Fs1a}<-*SwP3>PkH(v4K zZFW$VqTD{tFD?yj))UuDuehr{wTozrfFa4tefvZJrEeg6*%8f$-bZD#d{AYF2d*#% zAR3!~hRqU*NPptl{#MC5aRs;CQ^gTmpT_O$daJK5>qs7vZY1P%#jv!Q zS9p34^vl(Qd1u85oTmGC}z z*LmZ5f20`H0vIPq26y8DlQN-<4B1CB}AlJhBZ{eRy#z#yDOg#Gox#bw;Xh@J-6S$?Sg3Jm9sB(Q3xAj+i)uDKg_x6 zpBC;f*AI*w{1SeZTWrQmG@slUdCg(?Rwq@wQ0c9^VPZ_@e#oab$g`l6 zZ(d4*p>y}vH0wp6NdH&;cYp>)+sxWF@8>i03%G>nV#z;Ys^sibbpWw>}7t;wD4Npf74{PHQuyH zgG3}#UiL<$_mHT1rgG=9Df{c|N6pkGsI0K5aty57tVp{*xr?rf0Ff1-yINZ?#le&* zYzNUoycGXIdya(nM8pb!4S&JmQX{{U4*a^Q!WHe)Q;q9Aj(owChkCfXUlVL#+|y)# zKe7=!GEGSubTAh7+qS5YvtUw@hAz*;zhZ;8X`=Vx7f)h2=b&hJ>quqcMJr%j>wB7Z*E}KS2LjBE+0FJ5Qg6=a9l3lCapu&S=9cU05zZ#{ z$}fKi{yQ4#LfggoKURs_wuvcn7|ZPXS@fa3DMMyuhYR56)jP6h=}Ng@*b@DCbuK2n zmBdrV1Sskb;J)wot9g^%O6>sW%o$WvhFwuSeF<+kWT$^uz@FWu%=fhR!hJzx@&|c# z9N;zhq<9&wmI+v89aYm!mW$eLJVt184=ISA$gE?iEDL#3d-kH+1C@m>*9OmjhKm(h zJ1DsI=shdmrTU=e01X_LO1gss)!;XP3W8+)Bf~B&1LBb4ipr`^*dWQZJO=o!PWH|N zad5Ys$My={TUjjaRZimUz3Jti9gBlc%_Suz@?jzR^j5cH|JL^2YM4Ykk~(kEb$R94 zCX}tK`68X?ENjX+8$VAqfIYJY*%03JS@(BqV}LS@cv-0yyO0Qs*l2-pXFbG4Nc>Qh z(OgJ|c|`bbSPs(USlcS(V{ml0!`_Z9o*h?+7wHH0qr&|84^|sV({r5}Zbx@8-b8;O zm+jZj>7EaZ&simCTpUbjhylC2PF9cfOwT>+K4>oN4^jt4e2i|_Mrdeph`-U>TzMYB zU`wDkK!zkpWF23YxR?1H(BwWxMO>FYrjP?0p;+zfQiuNk1+&Ll4P+d{!z8>yFA0KSFvP1fV5U_)C0-k~k({p1rEO3z1 zvnTbkLH(>xa@R=BCDoRVhJc^wrgJ|_!Zdrm(Gk`3`ejsSkEt-L3`{C<^6y9EhNN_p4lryMR(QFi!hN1#g`}=l!AV)$*-zK*uBF1fQO+mBGFE8C!+Z`=F+gH^4J~JzM!#CQYs*iB z73Y^g4(h{yV2@ZMT3Mc4^BXNag1)8$K0F^?WJ2iSivpKPvdB(-%fTjZkD#aQY8BWE z*Ip{s>{)RBWvxM)$H<=)T|)wSvakUvu$(uCS_OPtEvR3lRrSeDy3KFlI62Wk+aU4o zS*+$$l+gz~ZJL*os>UY6-abAaz(a&E9eUm02F}12mM1e7E{n56qK%S7oHyB?L%-P& zT4inhHrquivJ+Uivyq0|S-Ixbgt%n{t}L3cUI6i}!3QSFyObFVvuZtGd&sFZyag!z zzf;-gAonh_C;AanHxL zitp|SNnlo8!v!+N73v#AW;ep)pjt=*_2z+ZPoUGTuX5{$wot6qV;v1;anFMN7L3&h zoC%QCuY}ALk`jc`1GH0EyOtQAC!yd6EoUsWn>(pWCvfYD* z1~pQAc1u9CxjsO*jyI2mco!6vG5csVZgH9u59PJ7Ktdt?6}du7e9GrQRL(+f_Zc*; zBUFzxrkIXv_B3D2Lg#|)T#fK8)HuFV98+`e+}(?C&%Vc;JS*`l1@tkXKo8+Dz`%bJ zo~al*hzthHckcfHJR~5<$3jd*Ng6`O3{WUF6ORH^zF1hw4aLLxi*TVh@xY(Qm`fXX zyf5|;!!9#r9Ty{|dK`Igm+0g;)_3L8Rz>bxSX+Yh!rEum(ic=gzndPLyMShvAUrF+ ziJ4F*U`id3#l!W=`jgajat%{r@EAYKY}T1Co=|i3_dIr9R5Mr<mu0W2gnE1LQ#e?ol6yrLBZF>4s>K28=uyltCI zczv`kwuOC=EKq0jz~?N@OSOc_nr5`hjan};s#Xo`EC`Om`wh80L~W+2 z#nf3FA0&s8{B2>0OsTjB^A1UV@EOa;;C(@arpY39xxISTQ5lBL$qonjj!B1xG<8GX z>C22)Enho9h-;HdNt^?|`GIr`R)MaEVz!HXDVX@xX+Cs&=Y@CClmtIUX1IjXO=ghR zvBT{C0?1CpYw~?bb>w^g2}H{b@j~}2-Foey(*5_gGz?Yh;!$2;wOe#4<*Kc z_LSV`={!iL)VIfxjd1YDWN3wkG5vW|1(BWEdS5ccK2Ye7Zj?I_nxo&6ZxT~_%9ub6 z3o32cdfO!7A(y2!PGslxUbl&W_A>C1QD9Hqf@Q3F>II_xPbX-Fip5a`l0W%lBg6cc z$9H)f=_+v7#=1SS7P=UbbL-8#I(UeEZN;BXcwZ-FFH0?0uo+HeX~_rEENCG9@mU@G zFEKJmBJisQistX86=bE^p`zM2=@BGWV0Jpsy6{9W%7%CQ4{F*EU{zMTXKHBSjJ3#y z;wRP`ZoTr7Z+6|{<$*t(x5zbm2)AFA{41M+i*ROVc50dk=w_64f);-@o~dDGOub*I ze)2PXW6MFlKlWe-1HF6!RX*I_Ma`7Ic(3(9{^t$Kq05QDR(6N4Cv}7x^|N#}5kf;4 zb(u|Z5W6uN=3|=}e%w|ZY6;jWvbTRF6{!XNd*Q&;7>+psPb>7|#iJ8>kN=>YNKD`- z>^ykUL>tGvWi_O%;jep`AI1xVy^Q@vQ`FI6H(2I;%x8em#3FDTJ{*|@BlB5Ii-L!d z@Ot@<=LnJ4>*R-!&{FaNv^!|{lpW96qXV}wx>sv2(7qt!%XA2X$$Ek^w6cb5 zdwGzbqiZ&Ft!?%0&xtf|@8O_ukTZ*A@>A!6F~)S$ap6rPjnrZ|8F$?^NJU%Km0>0& z^TFD`RV;E-L>cb)bkjbfGJDV9Zz((^ke=ZUyd%mRiXEsddK0C(L5ngd`$@$gYP9V*RDB&Mxx)s+o^;vGDyyY>cE#Y>?s|mB%evyJkO*2*+u8 zN~?X>HO*;D{nONx4w}>Oe6eZl`yASL zgx4>XuY*E)M3Fnx)tg`C#I?h_6HSb|L((BM;dtOPDLBaERbW5p`zAO>*|JVF+cqid z*B?2+jNGhuZ>Z}YOUesGak+m*Wx%}ZwLwr_d7sH@KjgM3!nR!4!KZV|a2nfk{J3JP zHzFxuF-=gUo`Q`CPp;s-0whRQ+rEocQtV8CB@859OcTIT)TswAn8M z^4>F}?T`TW19b>3kv?1O(c4c~TYq%S6EMPmC+GO#bwFBk2a8H2=NTJ%5-nVsT>eLz z;OuIn4^41hVo0>FFELzr^=Wr&^3lBovyu19kkUU4pnqO^jpCWjT%oAwNZ{M*=E5yF zxN^9w{suq?@HeV(8Z5;IVdg3BJI1|A!P=(J0a{GY5M8 z5hKq=ZEZ-h=rgV2!L0xd1lNX2A(M={RGgA^9)Zw?^Bq<0s&YcVhc^9gZ}j(h!iPsM zw$df_=ejoc9`B-3+K=!^1JJf9KZ;H&fK7EW$2Ox1_&EpKQeB{zhDc2{Q;Q)(QzPU* ziW|X!|J5LfDI>qfzMK-c#v}i7cNO_tf>c%N?FxrZ!^%`)`jJN16?mUl1yB8ce&GB$ zjv3Y+XN$+M%%lde1zcPC@6T;@tWsL<5IA=dzgHHu$@c%3NX$4-77|m}86`Hs*LUe# zlTmQS^^hSFL}k%`4=vplV5xbq+0j<4&$Co&US`)mc&}~8gAB(}NTYBZm00^XjuJOw zFJp`o5CEhcaxx|C4w7S|N45F|+)LZ~^;IoeJXh4}UNz;v^3srT;4z$B;}mW>A#2YA zPF8tADg8TX$2t0S1t?jSHaMj=DnkebLRK2lg)ZlqgcKe$D~4X3zF%uc|J?9S`ceCz zf=%K0yCkuBDZdppq^rEj10mQ?3=?#l$Cseo*^JtMgEb zW%`x za{PEJ_*1qSpeZ*0oQ-r@{NaSo`M161ZGO;fFz7_vDDbX9^exwDR@|SiorCtl@R0Gu z^`3xw;fHmdS3>&$>4TZPo}>Ch32@8IOTaVHDAHS*&C7=g;rJFz@ql@!IQ3aa)Y|;%AYt*xikb)y_=Wi- zwI}@b&-L>&+_u^)^gR}5)i0obifsX?EBdKF!xsmCd1xLPj!aImiHp6eoAi>837g!t zHZT&_nS=x(m&O^PppE~B=PaZdsRa%#zzuwNHeh}`Suo=}x7hcNPbvb@G)`u1oZRgq zW$S4_M<9gu!fU2{sEI;;Vn{I^xKh*&X3Ls@)Umi73St`yj!^X5-#K4YI8TClY-nK?Rq0n8|22n$qcq^hB^RTYfBJLEmhy`Z05tPCV6T^uPV9Jh%B<=6Mi?Yf?ZdKl~1rqRQ|Kzx_1Ps+^d!Q$mdtTli-Tts>U4?6xcwM6a;u<_zh?qpkGA;P{(vML<74Y!lRz! z!W3xzpLbpczVs0Cy(E5n(^@#R?K`n@7qhz^H5s!qh|WwdjT6a-X5?N*Wj#n_-_*X9B?fCz0}Nn3*Ho|@TeEW zZ#BEi2IL6VZH)aN(4S)R2%zlZ8UoEc{p+-cyvg{rA2R6?#Tfaz%oI~<;jF|^0L!Wt zB-mC7P0_paZ9qkaFXf*FCMtgl5Vyi<_|`aG9IzI#Zhk=hPN&-GWS5@jLi7!Msoh_a z+8qSh5D5}@DKk@uM&^4Ox-uK?1MExp%!A;L;lJ7AkEIPx;o<&NjNTWTtt{a#YdzoW zmOMTwXwhKq5&5;|9JD~(Q?IB(6Vb zW30=di$306pGlZcEw%!7$3G-OkKo?g*D4!>nQmtDwyy|(RuUO)Zev)$zmo5;{-zs@ z?>2?Z`q>bEy|FgtZ=!Fg+5K4e*uW>ASvD~C5a`ju++qAvW`%IR9<^_Du~Fy42mbCA z%0TfOG(vvBsg`(Kr^riaJpWTy`FY41^L9uk@tyAQ`-WW7R>=R50a(<+A>y)k+NDws zqlU(jns7T+VD@f1q=*n+p;eeE_8x2A^;@|4yR@1aqlRIgPnhdTH$MKI&{OJI7(A=; zkg%i~^8SnR$cFkg6G4+^Udr%-y@`f5M0`JX&V*85is&pS9sP9I9sX!81i)LVI}$bS{T_mV@{1Ck$Ru}kI!7l~o;wdIeU_I9N0E^$hqxNUCUNW3zv{ zd1L!EKR)Q4&K~d91ceO!Lc=c2|CsLR#IiqyBW(IM(}J1JE6l2`Tn1-U8yJPp;Sm+w z(O1_sB=^{H^e}YATHb)0NahM2{Q7S&LpRS})SYhDu63E}67&iiBD^#i>;@6to zvCIu0M*Q&CNdGHg`tL|TLp9!CRk=Gu13SH7j;0c4jGihq+(-Gk{*vOMuu(lA0jovX zA6SbAZP)lvow4|6i%;f}^~Vl%Q2%qBXRiYvn_2&w=JPRN<~a+Jp=&q0$*-F)9t8gp z898Bmqh|%z;<{mj8swc$bw%qp7R@P90n{QdJJMf8Gaax$CHh4{^gh@CTPb`PS$3)euw^8pt?dq#{ls#X~dO)b&v%u zbRwoUD$xtz6@JhBd~{~+B^00cDdJ<+c)48UQ)d}OX&^scPnm$$?BGBLS?)W)Z)aL4 zfB7frJ@Fo-@U2ubb+u|y-Bt`n6W=#1%I;5wf(%KCR>yxXp3wf{1n|;ER`^_Oa6-A` zz$4c-KS8vtC6?dhCc&z|uM%BGM)d4p=;Lj>4B}leFxTY^owzJ7M@2-ZkXKa3OucGa zvy-=ZMxL;8K@Qzp#88!!YqVX0l9ebfm1hQhIyGP)F5L%bRyOsm3pZnc8sGU}CL>1( z&oKgg44#g0aAC0dJ(t!#pz+?nkiLTnDU8au#e2olvR8TFLlUx{@mGnnmv0!A(o2Qc zH?`51N$|_$@S9H775#MCwu6Nt&hpKNW?$+-&Q8%Hg;1#PBp^<#yA<3)(FBN}suGW} zl}tw&lnJJJN}XML6CiIbE01CsEB%R|8IJYPaS5Sq>qd$wQC|tyWOU(2np4G_P_f$A zHOnJjWnzEDF*8>-2L>`~?$Ug4ce!!M_Q$QAQj zie0&};R*zb%$@f6iQn8DTIu^y$b(e*u#@b7ja&LrUV?n|kT%Fw%S2q~t-Mj#bO? z9+&{IRC|N=92wBOU*$R%9*h*(A4CXsVFBZ)vIBF`kjy^Qg8;)EEHhL?k-JIVm&&2l zyiZmQvuXm8hZM6Ge{K$x-U9WT47TUb{yEFf@t_r+1IUBjA4qXnDlYCPyz|wFO%SEy zLBo>YGDY*bvWK^a#~}S6g*pI`G)AHlPeZAqB0MUp@-^6pegoR&f#!7V7sc2y*9^G& zW=05Ip-%qm#y}zGU$Z+b4e*}@Bx0C)$qJ4sOxzNt87>lBQAR~1I}vBSt<*MLr#Ks2 zP$+KweN92<%<*2ChjVNR{#q;iiw&F+LWj%4X@v2|k2%L8xT!F6aCB^qehD`X$8VIW za(TL|$`XF4AdK^fwGV0kz~hz3Mij}P=cz@w*A~IFRNwCDF@GFi)?cNI;HqOMJaceh zcCJ`N+1;Cxfv$9J!u1o-Y!s8v|j}}s*yaq%4m+wQjFF$maunzGXO6i(jNCD(Pocw zz6W2D*7@K%fM1ITNwrY+mDE&|dnd4wQ;6l<@Lek}!>*Wc=@0$yUX(b#S8%)#P5`gj z?3UB>Nh@;BvQ6vx;Lxt#&=UZtn!* zOL%2b`Xl~jNo?TfaeQ54dcP^!)qnesi(ZqnJ*47u{T;%h1$|Wam~U-@Kr4jg3KYaEHp{1I}|1X z=R=Z@`OiF+iyEcDi%^uldERTZ=k$Q)aK_`=;0qCWIf-Ol7y8D9ml5^575`)z-SHus z#G9T*=^dV%OTJ4)&NeIysM~Jm8(u6O+WlFc?6I!GS@~RAsadH@4RIbET{0;2GMh6X z2Dhb)G(K-;TFRkc&Xcabe9!6a!k4x`!$X2Pc8FiiV`jsFMm`nZJm_mrh@o=ZwVBp> zn|ntBjH&wgEs33fE)WXkQ-?enCH#BoFBcM-_#@l^t^5ruzDQQ}b+y@6^}l-0`&uPU z-mxGwTRSW2=pRTDuXL`BNH%%ER@WzL8}r7hW>)b2J-Xi`osijzCCQr<(XK-$=l*5P zd7=w;xCYwd{tsiGG51en{?{47K0P`%_`lJ>bI^vm3<2XeE*PSDWdvhFUg=ibgZDmIiT$}WZXk8FepG!KsK=(i28%pmS{~ZH7Ns?gH zqy{*WVW`M0A=#Q(O3nCq8T8UJXf~)_LV!iBOg`2}4j#wkI&pF;Bg$9>yYv#9v^)F8!;K9^xs+{^uQphD-F z9h6ld!JqK>(Ue!GW~gL6X8gXOqyA05%U>y4-2Y`(tNcIBYAq*y<@@CjJ}gWW>eaN@ z#?&Ml=Tw=viz{mx80YGSWf1YyAX}-2FZF+(F9hmXyW;epQyVd7ZZ*m?)_2UHZ`_wX z(G}}imt-U5NE$tzR!$#AintVj1+>YIGTUM|Y~GZ=oVEYA*ETkPzP06FY@o@8Py%mr znOC!KLL6WA#47j@e$l4^qN(059j1~mrN;chfH?|(Pw2p4C0%Lwv;Tis?D)ZobMMTq z>{Ss@l>`C3EN1s=v{@ntAp9VC7-TK~CFAeJ%lP1utGD15j&{T}`yCFCHnsQ=2 z;Ao2?6K0|6L~(VF>k3=kvQ9X3{nS~DGS&?*;4jF}ko}j#o#fF!h^-w64?9vYVGYFBXL39;jUl9b`%o}?+=-2jt9b*bA8m#h8Uj}xo@eOF-$9JAJ=obGni;G zsU17Tn|6GD7whyWACEai{W^YmFc~$b5a&Y^=a|ka^pvNK;QGm2UopoaaMKKa4jJEl z2Q`fP4PR>4e7x493|#vo`SWBXNM0tL8caC);WcTXxCl3k@>PeUc`;C1wqG-KEXTiv zRKv~Azfe4;%|B6mfHf`Tc=W|K^*>QOo!mT+;Ot96t3Rz7MuY6AWI0Vcms1EoAr%|>Fc}w(mC+x zAE_gS?0h#z)uwe{d$03+AkNPcgXlo}PAl;O?%dw2KodN1a07ElkF8KV0v7)!+Y8iiWmfs9z_TNERDIp13o{t>8N zVGk5TeC@bZ9kqu4u|*Vs@^94sb9;ngsroshv=qHCkMA-m#8W;Q49?B@vF{!Hf7=qM zr2=wP&4p-sTft@aoD#u&D}kaqQh>w+Gr}R?RCct;pusRBk34q*7hwYOmmaj!A*m$l zsaaOlHf~})XEnT=Kye zV8u%43-kt(zB>XHT84kmj(C(ta9elPPHvMtAcl@0ywzkzP}+zwFCDt_X!Vu)X| zCQ|8!`ClwfPEcv$&#Mu%o|Sfa&FmtKL&6-}@5%a$I6bZBwf}0`l6|{B@6w|1yxCvF z7b&bGRuADCx>(GZHo)o?a6E7>d z(!ZWK=8QjH658Wz=Z|F*DfejLy_joWhsqI)DB)u*8rWU%%J7KqYO)E1gH^#U^90h7r-eapKNJm{wo9&cf45#}EI@ zQg4is`u>HQC36g8%WXxq6FPxkJH>yuU%RECT~b7f=61w58-K1=0nRb19_KE%(xNYZ z#a-%CQzc8Ki;d`EQHd`)AH(PUN5D>WcHYo8%R@>sV)9|}o2Vi2Op-kQSPh_I5vU%2 zEgH=W!F;bvz9yGGH@5wiBi%D=zVEM*K<;nv0ILW_mt!A+rf@1~+VZkSD?JSsqi>ey|qHZ_-HcnTc0({ zc*q8OBJl?G^Lg{D(hAoLd_Qi@Bxzm5B6b`#zeEBQ=9ank-vXBqJB(=s(9Ciwb0-|X z!Xqk2+?XxAIXb*IfJD$KIcV#XZqO;QptPin)3IZqh}ZCZO8yb%^r793x+fo2&)Oiv>sP z+h9gAIqgsg2K3=2FV$lowHov97jcf*AhkluQsu>0?ybKe}WJCgzrp{M}bgpoej>SjfI4!CNx;F~@!z zrJ9O%96I1L{bm9J{drCz1cVO+Bzp{0mWxW5dOkni7ucXV+xp*2`%C4Kzb@^38b$!P zJ1suA6dXr=dgFXokAF%~5yPvh8Q&gkha}^l^j+`uUQ=fSr`UK%~pKK44rST zeH1fNzlvFm2(@q<+KU~kD2M3AP?zgw8SKF*1vG+@R8ph(UThe@x%h=v&d--OC9#>p z)Pms-m`?tb9WLyTAF4Pd|GxQyes!E)bs=hf76~PhGSP_qV3zcmC(*%AJVN=JX?S}5 znU*x6R~0ZPm&y&u#21mrE7kmg6YV^(e`~2si~cg_&51cGA*d4$*ZPD9?)f?s0UiOC zcCO+imxI4wYbUIQkhM=BV9-0$=W<(!bDl#LB(2p#wnnmD75)HyH|qmWJ@aA*v9{#~ zut^^{`%_0F>E|wL9n7R zdIJeYWB+qPu=Cf1K%N~8Ng@BU<`gf+N^3nJARnmu#5S}ifz75WfwRG$QKn8bMK@LV z{e3)!8!5X`Xjl4G^SoBu(wCEEDb76d)OwC9)TNm4~`%=)byv}A$kk-iv zpKeF4pS~nol@Ad*5_6T0A}k^=ljPJS)RA{&a5L_{ELd1}BwAp9xH*TouQK;m)hG+Q zMY5<&v?878$)yv6xkxt$tzZ>n?wm_6a^(P(~c7=4%vB957PMdEmk6?F~k=-r|}96*izWu#@|BSIo#E z>EmYWQm4vQ8(vc(O?B#SSuatag)A4;KnZClnq+rbcL4*2MY1I6*DYVkDZy}i9bSjJ z!3Fk>(b>rF98BKDy3u(Im>)WCFLT@DVP?1O2nl%2caKU-2B1V4uELjP5ARu}_RRCJIvHlaw(uScRZ zF>0O4ehQ)=O>O zyVr|0hD&o5ts+=#I8Cj+LGtFyPi2IQL9ld8YQ*={=2FsnAHCX`FD?e*&*S1PJ*rT* z73s>pG52t^IZ1C8-Cn<76Xabwpr;NfFdb5YR? zduA9cgz%prhIvNERuXy)p`xAqF9t;Mcdqm_e(_!-;GfE|{bH+is)wL{RsSUr@f-3+ zGKS(N{0s~sRim5**8>{vy~%E--S*N$N?=4G!3Wg26LLK7;B_NiWC|(+@QJ}PPFU@} z#V>klg3WOxyX5@wI`7LTiturxUdFR)c%U{b@yS;_-_ZKu^X?+eSSKJWoD8)-2Q_N6 z7nVG8l$ui{6S<|JSV^j1EV{?RCg2Tx^ub#_MBPNa%f}LnF&Au3%38a*DPzV3p$=NZ z`+Nc(n}4<mB7tJuKtzFB+cXx-;oMRW;=qt?e@cO8sxVV;1LG zNSCB`0cMekMsh4*?MW^uS#+h>#)Yls7N9h;`o`8Z;V`ZFB3tP=5r?ful1pn-pt!n# zKR}E>y54yhcaq^8g1Iv|UR=S@(y_-(f}y<`u!H&a%J;O~atEIO*o;t>44O1DM3{x$ zH^a0|bl2HWM<|EDGd=lU%o?0|ia67JvE^6AlYa*6C$W(x=r{k|szo^+krA1-Gl~ty zlpV4Ixf(G#M7VjGtWEyuLlrCCr2)}d<&jwXcwkWxGG?^8%e`8DhkYxu%yk-8T*T%E z=~WX#cdMA9xTYt8>){zbq6sBprDEvOwD+G!M=Q90cpWDUnUY@?Nz(i3*I_eK$A28S zR)d{W@sAQZ!Oam+Cg%0EwUkIjB8#a4u-YriSOFD#KSo>`l=)Z(XSdqcjh7`<7#S-F z;n_ua^?nT0%Q@aCN@ETqITNYp4+?G&zx4dii^302J~z(&F0Z9|<#zC%{bg+7*20Xf zrND0s*9QpG>UphyJ6kBK-a{7O)>*46#YE0TEKZagoNQJVJS;062er3}EtUch`?S!# zA3Pa4a~S})E7vtOGRHP|E;$A`WH>#_)4fk)C%vFqW3yt!a|5^%aL&x|9sumwpP43D zE9(m>+>@`cm0upkJe35)8L{WRuxJh2>Z9iG-re^h6Wgrtl~pA^8BA6nJbO**W->Io z;X^xmGvtJ}F{olk{#=#UDXD19)!5A%8q>p#rrv7^GHsymbt8uTG-nK0j^uF+BlG~srFwL?Au5Sd=9$d{zPCKGHQzeB(Q z&jU~6uf<#~;uw$aB(;LACUM&3Ff3rvwY$1tl<9ZT>mhO>HbPBMeFbCSL6 zWbR!%*zIL-?%eLzEOv0MuBS;P%AuoaJMd{5yz;O(iY|%oh7@MM{=N*61xlQ}*s8|M zK`X|2Vw9W8qdHrw^pZsCPt4Rks3DYavX8e2vKFF)?sGjHen#Vdc;(7k-3_r>?>RVR zDezI)-i^@>Y$t@2xLto1j&N&St3ex6fcbTUnAf#?3RhXuYk9eUdjTC~8I1ARIC8H> zAx;k3*O|TSQ2o+`H}5MYni|95z5F_H0UrkVa#TB}Uff3mD}xv<2^%6-2Iz|;tLq)U zhynRupCzQBv8IaMWYVtqi=Eu~($uxUf?n7c6al_MLn0KWK!**tsvXJiytH}1boqBm z67n{C%DmVu6cfBoTc2BvrFgCcQDV6IgM#4y$={IQjsC^iNab*V#c|A!GsCx#f4utI z#5z1*l?1c&>$xbYQlupnh8rp~)k4^~m8GjpyOxL8zIEs5A5k=PtZNPXjH@qvQLXVT zRNQNf>u$8M5^AXluIoMK2-Fu={Pt0A4EX?>q{UTL^o|iZZ{}k+YDj;XbzObJq*nO8 zFA;*bG=VAED`KC!cdKgtwr^6zKcMO)k`O+NA;yhemb|!n7?m>{u=w3k6k-(-_%pdv z0L#*Wp3xY?Npp@o{yp)F?p|I^d(!|K@lQ*p^sz5&%03+Mdt&fH?Xk^9M7smcYI-bxb+2%(ubul^m?{`Z!Nv z!99@r^Z8W*q$azQg%!$g59atcnz;{- z#w~tvD1wUX^dG%b0$~$mDFj)V%#`)H4P)E*$e@ z>gX>1L(iG1kEicyor9!*JbV?iR5_78E-M3ubcD3S$92EQmL_jtPw7WC;Q2XX>TyDh zDc4mQI#@;F{D_f~ij{k0!!ND9l@xsW{ScPK-DMoreioN#!ykwtm)cH6r3Ki4-09f4 zjN1KnkJ+72H+*K_E<2}m7@(f^m`O0a_4$Cr8ynlQ>ixqEl5;`ynWq(_fk~2A_+2P; z<-;D{qstz1xNq7Az68oI6sSR}fnab6H7B9r?CtuCtNNkbZ|050+Tgj#Cb=OXV_ z&4j?srInPQbF64C*siiYq$z&1vWuM<7~kUJ7%kFC6Jql9bv_OC@?|5Q@#*U-E3ZS)p4?UfuBw+S+-zL84BZ8v{z2nk%_H9u))nY)jTP`|Dt z6Z@+N#@?26ku~eb(o8N9YOTkN>~_h>A2d-SZF|{wx_7FOD2ioXNv-7=&>V=)HBk3W z>yD(h{ce4vFfZ5;aXKCQK+IvN#!!>XBVY<({O;O5YZ3#f*D4tHpTMC$=_b5afn%S%BB1 z6Rli2HLu-Cm@&Yp;M?wZVavN9)teFl0e6hi-)PrO^t>BUVKyz)tF- zh3sA^+oAu63V2RJO2~QjzHl=RD$UVg6t1Rs#Q3$ z^ebDFJwJ%ox{_-qC2V?#+AP=x-!}mP85LnGwjxOb^yS+fcV{s<#hRo$K|%)$8{s^{vx!0GC$SjQZk0p2lr5)}6$t7`aP+ZlW}?D) zJQdqE`d7zN9+S7`e!D19^09MP*7nzKX({BTdxr-AW7BRaQg}FFf>)x11Xdd z)Vbi@KS^_%MX<1CYL$ZdRLp-+2pX2?5Ohv3bg+cUjZ1cP=3BRKCQzF4{@op!Jc8_L zFGK&7uIEfF9lZEEMu0jANj{ZqnY5Yi1GZlRsKbc@Ks4&5HMZE~U`GNKBwl=a19W*s zF+Sg-QZb4ok_;{Y9rp2{&KI-3oBqyug>;Sq*X|kU^J;LSV0aUJMaOM>VD?=^c3rV4 zMgjVF8Z0YN_HNj|8teH#;v2;|0@HC6Qmvp_;;WA+%oQpegvzjVEeJ@mm%$=0$e*A9 zksdmz_a};DJYv1w#|Iv5k_*BZT(5f<2V++8I27mE*-S-X`a8=6%e1eyYY9tiRydjq z;otFc%~72+d-k0_C4)x0&%eR2qX$*Sy25J@!F$l^LH`;j&LbixIj?qi*-zX42@#b3 zzJP%;SG>1|{4eTrIozrU>52fBy0CyXBbS|=c!wEo*E-cL@s-1!vE8QAIT((Y)D)AY zrqe;`8YhT@M*7GE;i=9}W`Y{)u8@as!4-u*m%|~Jk~>t}>IDivd2_BfVyD0h zXOI}K^?E*g0so8-5tCtm4_Y~Jtgn-rqhQXfGO?{#f@(zs6V>7-ySs%I!I;Xav6jg5 zU0fXkNhg26*fw?g)YLqiF+Qa{wk=iiiIOl-SXyi@&-1S)+QBnVfqUBNt(OcVD6jtm z5s1sJb5f1p@e&G~n@+|q!ze0p5x4;t`Lar?3*Oh<`_M@@ml)tv2qDnT&=`*eG66E) z3Kf}aS>3)?u;Ufg>;&7A5Zu#S(7d#%ZxhXocea_nZ8)EuW{q6&;B}R28mv6CGo^F? zQ>OOOWaii$V-g4<{1!Y5Kz@fWHLg?#&1V}mrnaefC^%_+x2?Z}8m>=R+b0vTFa6V_ zmbFki{oqN2cUr9T(Xt%>-BS9Ehmv%+K^Lw+yLJ~nICcV5^4xUJ4XAG?btGHa?bod# zHl00HiG-Bvih62Buxwee*d4PM`*}%qB~3q{tPCaJ@Y5G|46cZ7C$fOm0RKAs-GA5s z)2@dPNPP+eu&~$KCrw>nbu`-PhQpnSBN@MJ#n+7I#VqZDnaSny13q}l?=1exN$(>OR7z@*E)bas&M^z1 zq@K)Ao7eXGF38=zCBp5)-Shlkm5fuQCXdYD>;E0+%1UwXN;vPT%yPW`mXXW~VI7FQ zQ8DY2`WQECFg69GuoPb*`S>zFn)FGb9hb6pq>y zdC8zgJ<|$gh2N4b;3ofdF!gw2KmhimN*t|rDKif1_yOc!^Lu2q>SbRJWe<-qcCTS)3yEs= zAb|{lyaH$f;j)B=$(Rk)$?ZXrJt$wqUlu~#hFqwOpju?ZB}#QR{rVYl==Os(LMB$6 zigaVOMROMGdSq0*m0Sde?-C+N=CMnPgXweg+x%K=Wk$}i8R|G3;OIflj~9Y-2&Ha( z{(ofAf233v)((rG)7I+|$u!$>&Q0SN&IWgM+Y38NXeY&AF`gh_^7gd09zP#zb_xTM zp!bgxr)cX^!m@U33$5)g1o`ttu} zKkvUm{enbtQ#V~TVT;*XFOaM<3^p|$`v{TcA5q5WF}UrsYMd-HK{-kfQh#p3&RX&gs5wYblQ7o?aVsFwofd zN&Y7LoDXOp&R93y82akCy7Ote`1heFVZiiqolzF#11nfVzc*bwy^1cjGfqnEe_C#n zmLrcffdAOGhX6^etTz0zg#}U6F^wGw6>hRTe@FpO_upQ?Sz=sUF#*44xHP=oUFAU= z3__iG;AN=B7!J&ZVQ{ric*GXTIdlCNoO*T&upbs7aFmDSOpSUN7%Y^j3$uJ#R?pU4 zG?Fmk7LtaePcPRhBSPODu@)r`4#P0x=eTh0?TmdYbk;t%^-!t6aMu~6WIEQ(WWabk;=ua^ zqz<48qR#bZ^TKytCI19G{aB%%bS{VecApgJIJd&a;#l9gFbCJHO^EG|iZI(S`IduC zfEd=mxq+hZ{5U(UNtEu?FRC@-Mx0CHH;{Uc*$K(koH|yCM88BJ4DM8ZR@%wi^Q_>$?MU+A5h@fT%<eGJrM1?nj^ z0gE-BRF#z$-)u>R20k|^iG!lhdgNr)9A$@60jD^59T$lDpriQnEO<%hSR<916`X;p zAG&SD?`qI2!l<6LD^VlDHTZN1b8qgdZ@FITqTtvnwOsllPpj{?|LayJjMlB(r|Rlb zmiAC9PM$mJE=hbQwB30mF8wB5v-}x-eUwAV7V_~ z4WFgM&}}?V%}~uD0al*5;JyDtIql+YC7r)k?(}_DvUZ;U5KKY4OaXKsz0AdQ4$Aq< zd+ceoiR^YChKo(4LcbqBJi#~c@zGzLUR&xYGc?yFZOW2bOa$8x(h&{(M)#(1;_C6I1+JyHgeygRus`V~2S4@1nHW6~#HkP-- z@RcFmRZC}>GGxu+c6i4h88x630$2!B2TihX=9s}~swd-s|#FKg;u zg>))1`^8W)xTg5S^U?id3r=w1(IlsD?_;W|@VftA<#&jYY;P6Q{ZGTLzxCnW-^!_U z2iIQu*y^M^hvGbhp8BaWNI*MPnIU``Vbjwj0&at58R>lo4*el<7;kYcUdEe?*b<*n z*W*$G%(VJ;7U71AA^VcX&+&@g+Rj-{@PY$O-GDQ-1-@si_d<8^QJsGRmk385Q>&LV z;`nePtlvN2hqzo4@qct64uSl>){#47$aU? zL7{Txrvr%EvBho0reD1O`0X__Tck6qyYY1e0p<#)7XOnQwkW4s9Y?dE2aE0t)0C31 zy+C^WLuXWkMegenYBeMnVO0LF#J-s`4{>Upw`HTGc1`QNH}cy5 zh6!utbmA5Ye zv5OxdNaaBSzN#JxUERTyt0wS!=?-2)>cSjhQ%F`V@-#DbTo8~NYD$%!{KHUm+yz z`Wk$-rSWSl^hVyh^etQVr#}))ok#ZX&n>LC)C@-*D{`cJRer$ z8|5dj3)aYJ{Y3yUP*({!x*rtq^M9!$_T@+zdie`|dmXILYX|G|-gd%rQfxwsc_?{& z5&@_NfoyLS&;$H|O;`aWk$lVBV>tr_p0rv$t4galfTLSwd#2g4C#ppYb)#I1PNCUd z-()&j@DQXF^#&KNPoRG~O;XNC6mA8rZA~BZ&GJP;abOFRV9XVx5RBi25Mj^%O9-L- z5<-PO8%~-wVlHr;Z+IoUn7?dj2Xi?~>*NOStquE{-r9fIs{wWn|APGMXg^g?b^({sWEz)_B*zvjhMuI~V_6x4>&EY72SBCLPd)9m46VQsMBreVMI2>we6NqL=MinV?r zf8uLFoP0&Vp7;yQ*uLTDooS1ylsVQG$=WByV{E;+p5zl-(w{hk&BJ4SeN6hn%uBYY z3R3e!W*KXTR10WqO-mgbEXTK(`qTdNZ(_(BdvLgpg$m&xY|-GSSccBW=)}x{DN5MP zxAq@Az>v^aAyy*SwAH+_UpCSwZ9~aVG1QKJ*FfZPfBq%Z?TtAsHd}w0Fp>~ z#TxkE(JT%8x!PtKY<~^YtD?f85eeLM_8(Zy3@?4!XNMor zQys%Zm9qxcQRX13e|SkkXjDL|`F`{>3L|}!nFaH6IXJOJb<9ZN7}XX$=d;qCtKQ}U zW#8Sm+UWyZw{ zS4;QHs?1>qr~3N~-z^L}hM)L-Cg!I#6rQ=^txjyGLS=heAMIzXM!o9leHkLQ(*IuF zEUrDuZ;lqMM2|4!cdGV#C4&C`P-OjSPsZPhDBpRgzW>N@kuO@5D=KS{K)& z0e({}V7Y+PSa;fBi!5{vubsXfKR*?4x6>vmX8X!j!Hl4nSD&e|y5P~`nD1PGr%piwA3kc3;Zu~D40YT;nqVZFjyQn$G|F?OhmiCx4EQCt{C;l!xX2uFFtn{+g z;aC?=jABY>YJ`RF4Enf!Rc_3Rbg(WQxn~hAg|P8JJYCi+iivsk&a_)WtkGl$M>|~; zYhJNYR8P0~BcHAiRsgxRyT=&&?;*+@McQZmv&e#VP$EXc|62^z$&(DMh+JSF2_M1* zP&yC5PAK1}QSeHPk5=J>%y3u(TwvoxxIrw{%da};GlsU1<$@iN@8Qz9meva2iJMOt&t&*kYeruX`U8$aO|$)sk{MAu9}fY0=xHISHu_?->iU6$0Nrpwi9xfYY^ zEV0ci`&<-i-Dv@|21avJCThbjgY}=T-%pj5ADS7Gk5B`6Nikv^ZptE_Yji%m#PNIw zhn?UCuiR`RWjwY-HWO`S-EEg0ff_Jdp+mQYDkAg?^!UL>_ubzEK8v9ubFMoJFL$pG z1LVl;GnnNT1o!f<@qD{Y4hQYNRWi?;K+X2{?^KJBQ2vBt9(&NALB=vGtKWLHwk_#^ zQE;UCaB!DrJvV!<%vD}nCzwq z^hvLLRlZxQA8mEU)sM6R&mTp7N)5;ZVaq$ze50cMy_n&q%-9%wUFLR;@}&-^B`&Cq z8pru9R)U&GwnHFN_{m%;8EKLNlm*ahUR#YVWyaIMWRI~E`N3dt9{lCI8{tc&rk+55 zTjYH|V&ci1=2%$c3Du-T5|B>3dDvrwi-vu{Ubxao8S7tji9z75SSVnobA8GPgA1{0 z+AjSADwnHe!D~e)iITtSUoINWUFq;AY|Y3^>e7tGBzS>2N2aK~L2nVnE=*XR=7*a@ zdJw?K=WOEQ?Gk%%uCHCE0%N-zNDGipQJ*~dP3m-YbY{F0cmnih`^WX!jW$N6*phsY zP*SY6A5Zvc1~>1-Cc}B??)hl_0x_)9kevv-34i%53P<|LM#3Xsb4XqiPG>Qk_Rs)r zV+8!j%FGQI23twgZCoIR{NN)oy>urqM6mp4HkS7-RsVfBB_&qOt(DNiv8dIVtIYP- zpBMANZS8`{xZQP!Xh24i0md8yC#(y4$6@h!H*@2^F^u>bUve0OyE{1c21y)nXHE|I zLQEDqWK|)Ja*`L5;rv)^(RWPCOM*4X7hxJf+5Kv@qR)g|ol3by69#SFp03~7k_Grt zsR_HSH&nRXA0K>s+mC+G>?d;sRi=y-puW~(jKcdwzQC5Uoh?HEv`5%4dHqkPBZN*G zvFmgXyuo?Q`DEF$c$nci4X3ik>rT{!B|$Q4^DvbXcKl@!Mc$o=ifJRBxHI%d72!5u+ej+QK;fM&6eBeH#P z>}Mgi&(mt5RTa8b)+(P(wI%bS{PbYW%Z6Hbz{*_ihVf&14D~;Z1bqgHunxyn@z}ngL?_N?3?yX^u{NcMi|?Syv)?#BVMssze1)2n5YCSCh(+g|V0wYXL&D zLu5^#{zsD#(DudQd?@|Tl&hUC=Ld~;+K-zTkr%DgqiRSITyfDz8ZMM+Xb4Z!AI($Q zoi!yK$4f-`!y{~sb3bCPx3k%GXu!57Onuj~%(><6vNz(-1Dm~iA7Lzh0Lx=VbWO0y z`gi*!yeoje)K>1DJG?w)*v)hlOxvof@o%}w`$2X7sD*Gy^%J|_pbBtKCX$oSi!}UV zIfZQD)~ca-fpYIrFqgN~hnUwi@Co?cpT5nGul1XkCkzgOqBS@bs3EzPY);udvs*N{ zWfDneXhOh9pSi9dU6FnUp8q@k+R!$94eNYzSjI-<%uKvp*O6vssRW}=eMf(AtvMvi zWo**|F!E~dWCUreHHGHS-b)j0i*MI5+_$B#MXtM zx89dQRwY}XkFMSA%xcKlNZ?tS_K!e&X}fkY&SE0yMh zJs7!dwkf_lemS-xcq^wWnoFLOM4Hzi| zixYLe?_7OvUN5-W{p_+g6y1pl z5XX5)00Tk@7bWD&-yVlIwITFFJ&3#rq+5_H#F5^c@aR0s0)15}8`Z!|C|w2Mhe(w2 z_nH_Gw4SzV?>fE3v_Z?`Bz=~#NKGn&JUu!7?J5|S@arFAGcnGSpUgg8kHgWeLbzx^ ztOz-ycT+)^Ivc!903z$jwF+NxFbAj;zg)Y*-LB!d0`;-w*HiyBH*@RA%E~)K-pP^o zU8WsD+Q{wf7fqA1w&I;pofQ9sfP_mC(w0iS{2|b>xHCJWGXT%xH9v0gCN8YkbZK;$ z=ch~m^)pm%FDa$RtZbi2XOjrG*_cA~b-?w#SovF7?%P+hR3_*)Jj>Luh$Pv3y|>%0 zmRSQCZyw`dzYp#;!onSL)F$a8l7w05o2DA&N&3l(tLi?9<4S-rX7bB|IoldED%Cd4?_%hU8c;^*2O>81bZ-p#9luNav&~Cyg?9?@9q-WNZVg zU5UIvo!=>W-BGgII%Hce_njv23L}WyCt_@$nE#cp+Slh%S{>A7-rWFxp zJ#~9wruO?ck0E0&Sc%1iLbU#3>@{3e(Xe|;o4u17ATA!v)D>VaR@hd!Gh!vJ0;({u zn6n6*Al4D7l+6`Isus6vB-A@3#k;2TYIgeCtQaK`prpgJoq}-5VKk-3ske_1o`IM% z-XIcD@WIUX5tlKCGd~snk}3a|6vp|l&!NF059{F$h~G?4ePNQY%X&NJfjp>I3`<7; zZ8pIjpYTqS&jXTUH33#^B?|`7vnL>mnecT{!@Va)8qwZ|iZjpWDaeo4W zf|w6}g_neAL&f6R@ggA7pwn^6sw5tP9*ob9P8jD{uRu8wt9bQ6Mx#BpA>FV^ zDn@*2^tgy$;3vhJ`#jm)6(qjA8OH)A%UbZ zX}ruxgzear!+=X~f0X_uq9zy8Ml?~i-rr=PCQqz6YndM2C72yIqw6C4nTX%_q)eW9 zxmagWtS|r%`$XC(J4cy&eB5s`OQ)rs?nT3AaQrhBSWtV}&CTT83)HzjL&h0VC1DJq zI1=C}J6px2%9gXBN!oU^>UVbMh&)_>uQ#Vi2l#M|QAGyVh`XBFRsj+!YS;YG2cYf> zlJxMX*axoUZn%<6*yeR%V@*I*zux-;Vvn}DDvS7fF3p=U=q)H;RVeO1N+->e14^1( zVve{}64a!`w!c-11X~A{R>4H4MX98$InJcFamDe*2@<$cijRc!^@R_CMl<{8B5!3l zXA=O@NodArnS+a(SF#lEl@RCmyRQ+*0?4S??EDP8?e|k(nzxbWA9cWcr&Z&R2V3*S zGtMGkz}6YDYTTAkqXkgg$^4o2HpX$Xxlkk&LgNGWWaas{5jvZSd`zCT;uMUn$WKuR z^MEL*AUYNgSe=4!mv{MM{;Uy$?EPV8zKQj&k-(#zHL!`td%|zc74WJ-`Z(Eb+j{rE zzVBp&V#(oDy$pGKdMDSlHFwyT@IcksK>bRcHK0{Iz*ia0e~K|6>IZ8vnT*;>2oCUS zn_5_DwE(v10Udbyh-Rm~o)aE^Csp04;NQZH*Z{*qd2o4w<_ktG{`LX@Z+)yVa!$3JNyV+6`vWBJk_;N_Q`1Sdu}fnu&21#~(b8VcS=keq#|U#!jimQFqAd zLc*dRl7apv)Qa_6Qi?ssRH_x3>%n;PFJxT{rrPMu{y)j*lD(g;>b)>LIc#+?Ul0wp zxm}q@md62%8Uz!zA%qqF2JpF}>8&X`7N0@yuOQD(iKrfP2{4@%!~}4ENNiqvc=V3a zaa3q$pL&}vMFANAl{Kbd%+LZ<=0GPB6e ze*(i6q_Jiv)V~gsGm?}JQ5}+P7Ot(EBVf{jTi$uMF?(;s&jZz2_YVo8w z+bDuYU7boVX&L}6v#!du?*Tn9O^AmPb%7)H7d;yec#x!RUEzc_wFEbwLHXuW>XZhi z5z{Mt13Rd$wXkk@X|OR~mn?b2uY(k_st-2mFD;_BH9kd$Nd6Q@3*sQO<-W@s7yS6W z0x+w$pQq7$W)8VLpg*J1`}pj6+!rl~!uo_a0yxs&fSJC*I~Ma^lo2kry6vhk%gKh} zt>n<`m1KXd(#uE#%kWg8XiEfJw1lJhjz4;IUtya!oUWB3x3iz%2COFY%TiiMU2y9! zR{*Lt%X(u(;r2WoO53&wjr(m6iUGILMK)|WnDqP=7tPP3A^CyOt&&3<=Jn$eB)(<9 z_SyG=GnH&7i{kd|KFHc5W%6}L_wzO~S{Buz*KhU-0tg4BR-I1Nrfh>t6SX#}{ojO% zUbDC$;Bf1V*iFA(=lF!d2zWEX`ORowHhglBX`z5(`6-!5sjR6SHQOF6*j%bS!W(7| zRRKC^z{VB%5b2Mde>s<9esEHsGz0DAXocT)=SDgg`*88w?3(~mm8V95hh}C2= z|2$)*nGb(VP?5w%2fRjDe8K=*`8FSB>o}sn+xri#di2GK{e{(zT5-GYUcM$++yBU= zff45wUejE`k2I(gTu@-wP2p}vFtGdl^;S@`xa`rHxCrc*XQW6p7Dvff*vB7s^8UK5 zf9j-ETHU&DKHGr79pDZ~r0NS16aJ%+P_ zzY?TIF=nAjTgCWu7eBZ~jo-&81GWjRZrFH+5@of*O!_BRBCF-!DGG~#p!5RbH!i*5 zve}^oz%R9Yq0tqFy4u>!^X{r}C4+j|I8tyFU+BZ@ag9E-Pf(Zqq9DM<`b*|6tg33- zoL)=3Ff4X4>F#_?>wx_ce2BlC_H)L=$7S+@SkkP(66{A1yW}_!gL1H3_C`Py#HRLi za(n$3b59>Z%d8~?5+|aqjYkjjW$?7;zKWNz(^=s`a_{v z(l5)DTZ`QSC@$=Oksr*Z#WGBP>k0`k4XuQTil1=FA(f;=h=&aQ*sxgW4+@NW9O#2J z2?9se{zN97AVt~#id1Tc9@NCId8@Z(nO+~mr0loiRYQr=21aZ{wCLcx2?Pnx@5>Z{ zDhiKypG5nNAk8=Dzy8;qXvfBplQ z{s37a*VjH4+T~q8iPjemG@shkqnPW{lEYq65>#r>zv!k2M34CJp@eNxi%G9LPzerzB|h~)$_N|8 z{`#asIe{=*J#19`oz@!t!^4dlKN#$E$+{ONA^~Q#FYcWrcvXQ5egzLI<+bU^oeQi! zRr=#RcVbAxQa<(&Pe0|%nRKeAH254(udoiv+iKsMr6_+2Z0UJhTR8gdAO3j&8Y7-+ zhd1xNo#C3i@L=Dq*eYgjH|w5ERU}5!*tFIh3XVvI*^v4>BcIClmdUhrmUAL7)U&qs z*FZiZOGfDiH?v-%jgTGp688f3$Q~5csJjkMRRk0KEj8mHA+9+Vjk@)0=nqjK9gj|G zM7o)F2SeFWq64m-oLdR?@UO2G zX283Af@UNAL%OX=p>%DNxjtj_Ma68IE~wbOU@&FN%S&nI?nSF|N+5Sk*oloI*4<5*-9 zKkVg8RN&WM`G(Dt>TV~lwbWSAdgs@>f`&XlU0u?Fvm=P$RLnrr=1IDMh+B5NhLe%w zO4bpbX_W{K@7GDw7BVmTw`;>ZH|+Mb#OL(&>=A>BbI3Ki7O3qiZQjzA=7JxV(dh_M z)q}3@F2^q{?#3#c`0PKDnczyQXxLTZ?}(E;+{Hs&guBU7JS=56!BG}dD4Qn}AtMeB z-Mk?}!e}nAsr#rQiSRdP?@zU>Ej3g_Kz7k52WS{R58)`)qH=c5y00M*YdepjPxs-Y z6GN}5eJkGFIx9<;^QCt~nOfgqNFs;eTJgr-%4X6|J`sa9NNr&M;>GNee_oRx@hqsM zX&|p=Z|~I5)ns8#s^gmT`r~(zkgy65^Xv7nhx6<6Mx5X zN9gNb5yJbP+@aOQnT-<~TwqK%#>37Ea!aTzaS00Tt|rSjge_K!90nbPA-rLVB&>cE z+K4YU3+oL37}xYD&H=)_KQU0J{I&gKTR@;#!ITv%J`zVD%M8eb$r1J1^;ild&X>~m zaKjaEBs5s0x^Cz^udnpRuRs1dv1k`#$)^<>KmkDlMR#799(8da7RjLqs}faW*Ve!` ziM^lm{jAXNoLZY0g-V_Rnn$xQ4AmY;m5W5Vmjyrpzp7#TxWv{K%=e|_Z6LKY`0Y>9 z#q69P^xyz&)M_?ebx-;fRrzE7`4!_4`L7&WV9ypGYJoBwe@j~p^ zeZtx4Q_tB8Py2YLZA5Cdm(+30LgbbJiaD+};BHMJgi`kv{>EVUiJy z_|0ag67OcskD7|c0l(WH@%7@19yzn83>j|-$sd?NQVgG(&Udcs31@Mk^@d5)cDg8c zK7y*gdn<5o*LssrP7wyDVJ78f?LWDt@yYOEJ%K9GL#Nhcy^u^HqPK*N;&oDxLA8&b z5UqMHEv=YxBxJx)meuZ5?ZRFipgtjZzt6i2dp%ZsIC(D=l-Tx;M=qhXL~`=x$Hg0= z(&ynTk=Yjm+l?o=`n&N))*U8lq8r zB%429l6T8>nOcsS_*GQ&?$RVs6615YvnK`A(3fqn39T<#5=BUR_)J$=;p-tO+kND~ z=WQ$W)zIv!JeIcUFs%dXyNh6$KRyU!HnaPVnyIN>&c6J4o|axTtm3g~q8ZEK-|G1~#9^PIu@i|Hzfb{n^~HTj z%k38OXV7jJDf44u|3;CqUTwtvgntxBuzSJh)5!d%KlsydI{K=S?{iTG#}!;!gW z#H>3GBQ9mzfMB8n0bG{rTQS-#-22FoG@7R1+}00NN!Uay&NhdE^C1l2X9o{m zLx#`?zG951GpG5@%5-cBZt_&2r>cjT*%g=M!bshVg0cdgX*exhRsj#R@2K#Z*8(x@ z{6c|4`;sRr0832c(VFl@06HpIOheVw3r_|QEb$Q@(3=UF6v$zj%j`w1(XhGj!Mcap zcloXMBveacR3`l#wt~WR?;Fb^9!=yfxo|_k>-=;l&EaSG38@^CFVqbMYd%V;RB4w` z?d2l;?7PH)0VP?bdV=CSNNTCm@S3zn!CQ=UPe=0@W}KH0|vvA#nr;3Z%qo# zb03nInU*O@nUsj+{uExU`s%kj6B<>c6lLu zJO8BxuzJ{K8d8fudt%JA^>n zIWwj;;n*3=2Jzltxkj;=3ffoN5_U7F12E$ER{${AeuVW0|H>9h7AyD9l%+`6*kQsW-S2B(X9U-YSskAC z9N3fnG=G{}U(lajZAaUFLsG`M!{qU`Ns~|N2Cq#l!A1FcaW4pNY{}tx9tU!uFZIiX zQyg}WhRHS~mLVb_Vha))GBGx*rXej9lJ{Tl4NJq`JIs+iw>hsPy5>Z@s?}vfI8Fv- z=zsGO!>vJZKNI(oQ|Cx_oz(kLr;? zd)S^Nv~;M0`n9X3<7A5Zo~)bWE9V;nU~*3Y(^){16RVrMVG$7a1Avu|AHbt)|mj){!&PP3tB1qeXe96Mxs-3ylv~=64GUXqoc2 zLJu6^niJ!%s2>oQQ4*SxG_;xA!pL72_~eE8RX%xwA?sTj_Uttwszf$ZFKSEo5s?tY za_-GwfB)-wYoMFMlrnO-w^+cQ301K!fxuwZxAp>^n zlyLfSvw!Az7WD`bR-=CXp2IKTxp!%S?#7B9tHYGdL5tz#&cy;5Xvrsp1nZL6Z zaM)%v5IVdh*`yt1q-F$R5#mpV~uON8e)a>ln*6H~Z0&XS?2BbTm@hbV?Rc z7OrkjxG{+30Y75(lh!29}p+5g{saUMZ^nJ@lI*yV)!Q4jMzUj7Z7jbClfl|7FThcdVcrO>;zO< zpqy-`q3WVCxuYlJsn@DA`C_s}>IKNA(KH{qQgoqvF-YGB@MR;J2tIO}Hhfp#}g_?i`d=UiV)ab5tY>GG2aKrjk4bz{Z z7AYFWXSra1c9l``|0oPRqLz4d#N1jiN3bk#u;KuDaZvz}CP zz*ZiSbX2x>r_EtoiKnZdQ`hh{MR|0eHi1xH2$h$A*6c?wx=>OhA0qoux(@)$flq-I z5;c;Q`o7YzRDGAef{3}i^V?GCLbEw%{FN2(E&-}G*7QZtDA=j@Jv)z=5ot}a2}buG zIPpeLVStA)plqA4V3%IS?2^^C5E#DhImhW1Y4;Ate`M!;9}WaMMa7Q%hJ8wNOzUpK zF2a3&2p)Ir3K9V1Lib3h5F(Zijb_rIV&f zxEOVoRSp7n4-@*e4-*C>HNlN=z z9k03Id!Ak&g|}HLdCSH@hr3ZPV;btz5vn4)(UEOkdT#fHx7ucya-r8#)h;c z-Vu&+gc8V={bXD)`0dw`hi|>{LO30e#ymsQZ*n(<-61j%@Gk4NZJ)vtZ}E6(MXLqX zB)5=1V61%os5yU*7CfaU(e?R9@?-XzaJhAg+@B{oRCxi2^yKc0&2#4K%U%!}D)66| zUf~iBwhtOL)FysKDB$kdQTWqgg{e+7))^o`1IsXIW>8-#Do`a#MeBf2As?)lv58`U zeWa-Wrpb$EWyr{14CP94y^0aWx(Bltxo!nEWn z6T-6tx$0Zn1xbXhb3gz>0r$qk|E;_{yHj37@Cguxb_bwgIqBIm>!k(=?)<&lZx#i< zcz)kYk4O))r`NiEvO;{Pb+w`S*@cU8zQA*C1XcJbhG=yP`Oo|AxPSBv9l z0#I%Ru_pK=A!{Nz>5b3abgwbvG0NQoHVXwI6IR$;`51j3OY0N9MH2#hAhT}D-rT)? zQ$OrU0*Sc~pkcPMa84_OY}D4|D-icqJ_r@*1E8h?@Qi;>`=cyENk_J#rFGz%$X~(Q zPdg3p>(#!0_I7r5A|GTb7}o9SR*og%hk~=cA-R7ja`)Spq+o>9Ld+$k2HH7{`m8!- z1hUT0hP^Ivfkf251?CrcJ)rG0TXDmWL}k^lU9B#}2$L>GYQStZ6*0@qmo*KeDl7K% z)CNt!Ics(fg;q7DO^uIZ(}gnc_rj#dFkjbkW>nKAZJd1i$i_H4^Ozxsy@4cBT$IvODxmd(Ct=>vSQFKzR_ z@kiJ|r)RxJ5Tf}KaqRh=$1Mh`U*|M)z>~&Lz!t7hp6XufVE>ix0(O>a@*qYuh8l-q zmU$`Cis87=<36U|Y@Wo3891cVIK<^gaiM9VA8^@tNyC2E4Fpo!5YFO(r~?SMJkYQP zb4mMIJ&MYs=H22J0oN1GLo0?l)@!sK$d=h6``u$TDR2g?(K95yiP3K;nsq}c5?(Q{ z1n9QLy0F^_Wrc=&S7ArME_*MgD-zBg+x{#HGDX+w6g*~cSObnH74+=!NsOPq-B)1E z8I>xqUk3Y#Pl&p~jo?ciZLXB%N5JWJONoWDmg|?xZp8Z=H2f6Vn3w2Z4q64UqSPjv zQ2KU4Q@#NQF7_|^xv!;-zKZNiaJ=LR_1LWkpuYkaH4k+E+K}=|zy!e+uLIN6H~w9L z&PX!$&khjwizZ=zjl`jRhErgAa+nmr?rzOdmmO)oAn-DuU_NCy-*(jczHxgEbnWst zXwJ^RPs9@+$o`{Q&rIHF)>{KNw2fxyEFiHXA8J$dZ`EC&t&w1B)X544wK6ZzOg63> ziGChufU9K&vPGl!mVaU*_@EcuxzL1jA2DAUb$vJ4;RUCfMm11YYgt{hrmKh=Vhw9S zXXcGeaMxeHhjN5$)i<=wzwr3nNYRFi5N#e=bI3m6r&>^naGCS@wcbloN z^>QBJG&6C%Oa)??{~a*S5Q7fT@5B*8xn5?Xc=}cb%PoADshgm|w3pGNkQ_f<`u|IMJTLmK#nP9Rg)EH56#a}vMMYucFm z&&K88vf0V zM4`btE!*1GSp^WlKwt%o1ti3&&$dPbC5*FOMpJ3}KCiow%0XNcPUX`d0R7kocP@>R z_izjjal+)Iu%Wx*DrP_GvFCf2rK>?Xh9#`b((FJ>?0-_fy>%q~Fgd;FE{*G9H)HxX zn`}b~v>ui@_t&dAL5AN2QC4(ZG_|vuY9sCwW`n4Mx;Gjo3xpgiC?yC$!?Suqi=2+$ z8HH&8$!l&srYV@l-x<2w3t2h|UfMdx!v>>v*ChRXxvB2V@h}1Hsg);={+LiL3CS|n4$_0M*?2^W<_Q{9ALsABw9cq14#bNvSBA5rEnF=v-ZrWj4Re_#r3 zf(AEHB*<;zW6OGx*3&HG`$>T)rXSD#tzG~@;m}CzHT{V*5Ku;kC7P2hGoiYO4KQj; z7NftkfFz%J0#ItGE&zmHj%?J&-@UY)YJ0n)@mwlqdM+LywPJG5nDD)v1C&49DBU;L z+?hkH#EW~VIayjz&Q}nRb;_lZGcHNMPx<%6@#Dz|kT)^`c_SVn_)YD_Qs>7X1_+aS zjMW#`9~x^c7Y4U|zPlNKWKx@1K|YWdrq)@1WrV*{L_B=(jNUZkX#8i+%QQ}qiW^owk}KKH4Xbv>V)cYJ z`jX?kys4(zbIHdNt^v5Z`v&t^-Ni(1uV8CMTVzf|UCJJ%Lvg2<@=K3zVxj$THx)ET zPpI^#&B7k~sl4b=8P;vqz~k&0r0EEXP7U!^JMs6+N#y*INu}m`s_TVMksZG-T^)=<6LOR2 z|KcH8xAtk~RQECrNulCzz6TfTe>vQ?i8n4gXF?Mm5%OJV1sBGJaf-s#FPZT_C<5S2 z2wHJRo~9aS^wHYY_lP&SDJ4?G-0_kaj_-SgZ;LTwDBp$(zZEiqMa7uKf0+U*JmB@y zf>}1O0>#1fqBUAW^w8lzJP20G?c)Tj70se26{^8ao8KZMlRi-)F-UPZY`lGB+MdA?n8_6D(U*JGE z>z2U~#O`uU@YZ521?@tRDuC$j)FHnSYYm+`6+hXOFNsJQ5%`nQokr7rw3PeDt}byP zCo{|+K#yY0$X9t9qQ~z^Ii9(S1fk1x%S5gGqlSbd6~6>zOYFldAO28I@GI&%yNtl| zPfJSQyir%b=om_wIGOEnd%g_yLETdKj~TTZpTo=w9bWxWD(;=XZ=JvrvLXkoY!cz@ z^k(xD(=2ziH%o&hvL3t@vi*m_X-NV3q9r7}loTi(aDHw#VCK+knqvIaM9;)P8zFc&e9Dblk{r|-vWlJ^lMgVSTq5cEjm=4FXmab`}aW6&YlR zkLZ8-#A)T#=IZ8uc*1t_m~8ilV;fcioF2uw@z8OCt^)_ z=@R4e>TlY}=I8-~vswbu2B*V+)^~dHZc;(N>Ni>9S)dkQ<>xP&{xNqxk) zAEHLgu{;i%0+NI>to`#JXb4F9*@yXGI;Qf$RQYz(G;#cCghow*_r81?1S?fHTduzw zpQ)myZu|q{N!Qu01eVhP0_z+UhbBkJc-qDtNT#4O|%(q#PM@G zF5USHjMSg|=I!=uoJ+csj&0i+fJ7*JWDNTV;7DQ-4SxrCyRm+a6W0oMf$+cd*GMrH zd*3WYpigw662Xkhn|B)(LV-6X1GV)U&4fWQ-DX8NI?2wwxOU&= zcV>xKpK00h!^POYyxsJ&U>5#;eEQj1NNd7pJO64S3hrt>ZUJUP<%7 z&Z0nVSE+6iquQrv0@(&>I#;wPycpYfXD9fkE!|C7;h?pa%%ie->%c~bw4>yClJ2DR zZ(@cS&*zww%9wJL2&p%84M(x8`nA|j?9_xLw4XA{TeL&ZF-e~_)t886Ryr3$Cf%G-)|$mp3F>djE|WQzcfK3)eip4QM!TqAPmEw`V`T zTx8xY|7`asSTrp67Q5gDV|3-Z_%QVB>Ll9FRkcJ!$7y=a=f0`uJ5ZL-^2zYlGQo?C zrIAM7A5%t83~kw!rNw5~JM=wYj}Aw!5}czLUysF-JJBn4TX)P`GO3uIzk-}uQXRC# zCYR3i_q#Kv_9rZC$&`EUZE&ki8)6_TCpG&}w>+b2QKAeQmsR`hs}Yb&*Q@`xlx8Y3 zjz#x`{Z1C5@TU&)yCUY+57xhJM3ok9D4luuqerA&rMA>6)bOsU$|Eq zxiXieV;Nhb4u3fYr`uaAy$vG2Z`$2@P+t`#=Vx!l@(y@H2b; zBx0{9Lzyp;nR$@q%}15I^S2Iv72ybTy^#p7ildQ%3%(=9kMp(>8>_mhnxv^>Pj|un z8Jyer3w+YBT_#Of^Q7;1obGa_JT^%!Ejc5^kngIodan4ScTljt;s)yB<1t@QFU@?+ z4ESjiNj+C=;V2OCusT#ud4ogS3C1;)2$6S+CU%QAWUPh#0EU3Zd;|Mk`5B&>9 z-)FGWUJtltjc(_(eKlewxz9yISZG=T3T4o~2UeIX2mgr@C0sVSU_sLR7ryM|Ce{Zk z9n1VL1Kv0q((8wRwQy2tZ)4H8FoxSd(u>M`lRbvw;UNs!Q9^~LzMD}E(Qr6woLq)Q zWS|*ko5t&HXKIgBuS|9f!_{BK_4KgqAP8Z%RphGw0~wX_v%H;@#YUWb3id8$ zSsXe9YNkwr1ZQh6gdi$So1XI=Wtj<1ww64H{Np|r+*KNX zB`@-c>04O;29TzD5a8SVlejR2APp{Zjy!;t{p~k0b0Wp780PVt4$yCoK0h+oH1l`^ z&;iD<_g7~K!P6a$JK-*2Cq5n19;FlMkks4w-05{Uo{dvuSf9`hgZi=z8<8^LTfiHE zCY&#l{F9_FE{m{crTf;=;&_|f=)H)1i01zOstLRrARVq`I#36aTeW)(hd;FS2H9`hd!hk&(@C< zsIaH~yx^;({v&iG>cfXJ<77F4=94LGv-!SwY0Mlwh1`pTMCbP`jHswe! z)+ePis!?+{LQi(axa2rh#73fS&8|1Do5;cK3=zsq zAx9z?+fyA8ftm~>NHtqmyFvII{I#_%wF zrOI3SUm#QAOe|Em@@12p?YAYv`Y+s-#k<1HJfY%7!DZL8KlvSvts@r9KQu1a3wT=ZByl%2LF#FMkB57lEq$ILdj?W@BJ^lDW%na@TOrN#(eL7mn*XO z;f!eFb|0-jMgr|x3haHt(-;skn3U=ak-}I1lwv#Djj3ziJ)6-wdn8%&;4-6q*M{tk zIb+$F{M%kH{WP(TzEzU^hOPbDxc8Iq(kDI#H({UK-};97GUX84Rnx^UkuW|A^pHm% z{u4V2!5ZutB9(~;dZ%cDt#(T9ciHcEe!^Di)4>yWu#*zIr9bB`S8F<{vqJ4Ybe&fk zf7Vv`AhUxM!(YOQbp2|z_MVefel=!t{#!u>jUmbhAZYUztQRJ^zDGiY=x=g$*@*hVFWDz2>Qeo~s{CAa$zJ(Vc=HY&HiAWViD}+j zkIa#=(x2;$El|uc_vgSYrJk8~xr)e4B&#-~42AM-{wF0O&~1)y5^Kr7%r#6lVjG>P zmdEw9`ypRtCu&)ugs1;s%$wwx@YO6uN1egJWQcWMHy*=32~t+lj%4Fp7tRknAYp9| zlW5j)*@K2Hc%bxac*T~lKYTKjduLgd+|4v-zvw$Cv?QPY=lxR)FM5(iugQ zux4H8STv-s}wx6mTjYn>iLW`M%sc4g!DEr#RXw8JtUVzq* z9qL!QFNW=|8FgagadU%Gnk$_7x#udCyxsse%PLcP=Uhp!mpD@QeJqR8L$i^iS16sD zu8nt;cIXbkgwAJ3Nw*jPzIB;!*E`Z{-q8;>9*OrwSZfc8Fz*8fB{UTv@To|8!1`9Q zRJY^-PEw`8GKZJ!XP0H(JKOI;{fa-kr=zVd{;(Ko)wvy@_vQg~)f`|jk8dSqD(@7W zIS)QfSbC(m1l|O^BT{EF{Ip# zw@-~%uxN$A7M*;@Z*|0pK1lw_i#^45?d>Vi{t%}3{WoGeo4BgLXP)i_nwm((aJ>rU zgjWfud~N3PDU~`s#0CQRZ>KU>lXGWCi_9@%CWz)4o`m9d-CYTHE$lKz$l=bBudUX_ zPLRV%d-cS`QiQeiKGDPC0DXjo8_MQ;4tp;>P*bSf;Ma=}gT<#9wiBuT2kDG~=u7>3 znme)pU}(FQSOS`vB~kmr9N|3Qk!U}YILL`|^IOX};g$%Bdi1>20cZyTQkZWk)P=>x zQD0;Ggv3Hz5cO-%Cpbm#qYSNuq0639om!hY_xe_{;v&_rML|drJ7hO^@(=S6eP22H zptyfMot)Fk#XlsnzIzcW)))(`OH}s%Gn@LCvF*#HCBN-z^_qXDQ!Gt~`VjcSuQ7jD zY1qR1Ig>2#etnmT{%CH~%`E@<;4&}|5TnOX2KVpAh1F6$q84})n|^*veqK`UzzS)a z+kcUH(X)LYUZU|s>2R(y?acyH2#kCu5)!3^FO2Ctq4h@Io1F^=k*S7*>ciJKeV1w?E z@j$YL|0(8t*hh-9y%zxC zKEQedki^@-Ya90dm1|jfJ^y>Hfv9lLJUyY!&h$;dA8Qs+2!x&M6a2BSoesQ$_gcQe zR$usP(=cj-IBc$KoBNT3oYehyd?H9wZ}dOrnAK4kp7yuD>@_OJSa#z$d?fMue$S? z#g424_R@Z=5)VpkG5Uwx4Y`m&OOJDSm^oX{yOlrC?X@!{+ol~Bw#`d2CAb;`R;PcH z=22_kn43et#Fwj|E&s#29X{(M3Kq|OQm&j;yH+#fu+U%Qx+uTsET6LcceWL=<-WFd zJ05$dR=YzygPJR~4~{AZ><6yNukbVxV3(b{{v?7(`^eh4fq)Yy7VQVKF1VPTY~WXy zY504GwSNHg^|=M^cm$+8v%!a0=f(bpQ#QM8tD9@lUo{N}ZGmOduBA62Oco_zE(1K} zI3y9}2u;6nu$RvtoUKS;;N_i@Vwl69IaXgm1u!?8iPfF4%LOD17MxHKYpmPCrq=lb zfgky*+4V60s)Tu!Nj-;!Myhu(=K71;wZQ$e7;hybiIg9$?f(c(2L!*^^sH8CJIODgF3cHnKj*1|W)#tv?* zAR`YRmlZfQoxmfK)TdEcVXu^_80B~A>C5`pNIY=N)lU@Ez<&Vtrd};(!@19X_q)yK zlFw-s@4L76yT_X)Y9dgp%zW)E&%0+0J(gst>^hXV5C#@EduYo9Ay+>|sk7jVunkNe z+b^U~_n0M1OHz$Hen_m`W`1$=FD?9hat*gAsis8P!wN%wm;7ydX!j@l)`w{~|I|~g zml+49_RhShmeTW>jDzA*STxpiM^sz!!?f3Qp?$R$vsTPlgt`<~xw_4Pmc8`58nHsZ zLpe#;3KSF+`I9-@0mrVWnnw4Pa-9kto)hbmk6$K?ZTq>KY@ft%ccs}rf3Ujhy_I%d z+Fj6uyF5&FX-=;~GPr5C*+kj8!F`S2`@9A|fx<{qfpgL&{gq4B`bzrK1}WF*wv=v$ zMa+6o6G=*mjRhleqZ>k#=ptuDnK(8fWL7L_a$|kxR_=)t)0_UBxGm4}tKk$! ziLY$I{^&_A;?&d~FiTE^s#uBIP~nd-+3oCZo(NU0z7+U_s?192k_0;zSQwi=*oPT` zp34P=iw!YWG52z5L(_g_x~-cG^k&pM4BY`Y&xB>o2-`!;vY#<9{#@lm`uRPB zM?%J`cil}XEG!BE_WW>KyZPz^;>{C2!6(a~T1`0k7X$q>8v@YR0(!3BIauP}%%NUR z;FR@;1S6~GzI7c9hGJJH^WGrkAo*P+#Qjl;{N z;^R3#i@BBfbTrGFV?p9v_5i>NF)eLRVG`A3)YND{#A> ztoV#1Fu!?63suPAb7I66Re_Ckvd#5}h%sG`I$Po1pw%pVGSf1?o=-4}P1`p?A0f0t zxANaOGFiQNisLW+f(dSn&~JpYmf3Z{=#x&}NB)Lu+&%m#k1dkeg@#w!ru&d;u8n~7 ziVFUSW#af=NB>#<)Xj$!UA-97kmIYS$IW~jLE`6bzy2^26uF0ApAZkx;RJgM_B56{ z#kd$@llz@U<|n$XeMB*S72<}6Ze4})P~AoBz7~Zu2PkrNi)376vls;q z=j6)0HlzG;Uq_G>bAIcv|Jsr7X14a7cmGq)V8xXhBZmH*bUJ431b=k4Z|5HML4!7; zM73p28!GuVDkP*8ZVy5~NvEXKkmb6hNHyG@y^se841A=xVnNL{g!LcaPlD%JIpbUJ zV_i#LbSQgV&h1@Zq(T!-H^Ff_JvfD8Z_=(fi~Phf=;zyE;(AF(`nt;zkmy1hXJdQ@xfb+T^|YEf-FugRY+ zLY`sEZo(N6i%5NV%f6wA!sIU@{8a8QEg)ODxjOV=L-X_YohR0hBK7BT_|;{{x8P4O zy&6whNJZGz@hU;gHkRU+8YvS~dX53zz9vq%H|WceJntJ|jbxlNj-hBPX<`h$eZezP zi+e6EBg?TK(FdeQrT+8$_B*ertV%*cD~b7nb1^PM&9CZ{Pk#o!Q1Y}@Bp=Uus+{vZ z7D0Y@n^8M2jDX7m8@nEw$a$X=LvHO&IB*Car8X-+xs*G7=gHzbLsW+dn^kb*BUK1Q~tH99mmcCB*nr<5{^+m?YHpyy}H(|@$03q5To{b;M zk)bjJ#o}09Q#V5PS3Ql%hTT$x{x*d#6nY4h%s(;Go$#Cu2CP;SIhrDmx7)>260%S6 z6!4@99HE>Aa9z?7k(|W7@BE-i&(#3MdO&<;LDSL{Rk$!1Mj&zGD3vzl)Qy&)!qp;* zGwGUE>2*}?MYgNK<-+iF=gEh_%ttxfi{Xs%+=TMJC1y9JU$7@boe{CVG)H+Irn?S& z*m_Lclj2Ibf<8^(Kw?}{bV&D>>nQAL{Kg)^xuad~h;a>P`S z_-nKLlOav55Tp#HAjOs|8)glc{+v2o7A@fYex)xX4O-uDs3?`m%L!N5f4HiEXrpE{ z!*coa>+%fye7FTIJTi%BodA5p=aBm5$Fm1!73)oP-LXU!A1HsKrF@8i2{I-yWb|F~ zd}u35+Yk=j++Fo4(5+pAI!Q8&^&DXM4sx((X4+?=)iWyhX1mk9&VYM07UuM)jIy8U zbKg)B4pgsmV(|gDswxDG&KkY%fOSuWP-0q58m|LEsPNbgZYGKn8@QoDwK8|DB`%IF zW-cPezq6Hg{w=7f11F+*D}QX+XwLo$W2DKoVm7F2`)#b7M1ju`o7+-LZ+s8-TG62^ zHDY4UH#L@*qW(H-m@}k~&ufg*Bzpw53?tcYqvY|eEDPC>l-fn~-d$GMsDpu+gBR*2 zfg5NdPXt$Sgiz@9`U!((F?@JN?TBJq%`fA}H;>fjdDhBWwEDf{<%ntDv-ei?q=;Y7 z`IT%|ofO2Ya}>s_Q;TuFE`xVX_u>Y;(xcHIq7UJ<#8yrSa=97-Vsn+NmPd;HP6=*; zN1FQOMYvXQgAvtjnu*#nW&Xqb&n*5#*rrzmuW$SgQUleV* zd<~HgKZvQTbT+6--*NEu+W@0v1#368@2xlB0(*_|cFWW{KPaK%aSPA2hNsz z&>huv6*MK0pG}oXtH{99Ba5AuG5V=b^~~V>!fGla^?R<)NK9M8cl4!o;!lF}Ruf;3 zFWAPL2`aJF`j$wz^+UTd)42FDVaW;3DNO3inTX|Y_iI_8N2~|m{mJQKOvhH)-h0=! zn#_dAFZZDj#xF*41{^TQQ`#FPqThWOTszQ}ckOVhC2KQvX`>UJl}66gQ}WGK|Hd>5 z$Q82R>}OEAlW3pzw7kBL8kgz&iSRO!F0vsMI6s%sXs7ah?>(@uSF4Go8N8q!Num~$ zTHpe3L-4fi6DyRUVguYaLKFu7$%S&x3e??lqh8Aqym{q^Llgq}Se1%%xE^{%*Q7k^ z<|aL0s!HZJmbP<$ewYE}VG|Wn6kj{_;brEh`egVp(S@gM^65hNR+Pa|)~yq=e(Rsv z56UGcPHCU$7Gd$dD0JS^leX{GF2*%VQBBbTZA*#{MS#BtB)#X`oXkSvyVFw9+a(L_ zaNs)xrT4qf5;R2DbDP7o<9G8BhiknUzC67b>V>iKJbobuRKYr>iDhw)<8dY%+nQe! z{n3GX6a4yPjjU?_PxJF^?&W}YdZ>XOIZa-YlCEJp^!+E?8z)?$_#00}@$_V{QJ)YW zsf3<53$$w`CN6q8DSU!=tNQdb!;y;=F&*F@gM2TB;*0eQ_F6t^x*OP%KF5p z4U=mp&_yoAz*MSY&Gc+pgAB>!^>sY{NcGpF^>vtw3fOgagZSlv!3-Sj%G2`@j|F6VtRyr-Guvj(Q@(!&br{l|F%h3El%XH@>QN)& zq;k2@r@$$?GLKis_nkr+9`hS7c{BH!-%Ia)tVU8H5_VeFaZkK^KCYMl3%20$-jmB* zb8{=<0Ajyu9)wX)44Xd9&&PNFcv}XDaPsS3%Nojqro7;HBg9N}*DpX9r8L-V=@Tg^ z{>bn82T?YfhHn&1!yY^pplBRlO}$)xdR348DwcRR?*)oKXCf+p-leV!Jmvo<G zSH|%Q#P8AGq|pCtl4qE$A7<_T?kWML9!Y9Og6KPnKLq7+l!IrDQe@vE_szC`%sU5W z%H`iP3WiT8@c2Tu&WhJ5$;f6m+IB;)H@sskXrKO;Pgut!e%8ggCC!Qa^d+>SVpsS(tEc$$+dGC#Yt3n)) zPFvd)`kU5~kn2YU4S-T~A@PafOLbn%GrId!RLifXCsaNvV6&Tc($L(*ZTtxAK#CK* zd!?#iZ0s_cQ@<)c103TYS{~vH1$02Vet%OW#2|GOn|gr(Qn7=~gTKNc(w9~Lkez~r zzxD=3W_`I_b-;LO>_m-g1bJTo=K72RM?Vc}H@-KoSJps4dw(bEO$z(rat^%#gBHW+ z6EOr6tS6a}M{PIij(3+97CQpX%Dc95dh~Pp2ivB$RdwWgo|NQ%v}oHyBZqa!Tc-UE zL@(4pGn9H?8U#QiX#JlU=WQmU3J3WjxUu*>nXRx?_A&r<$0I>0#Y3t}?idhvqy#;b zqQsj4+y3fv*Lt?`*R%3V?HDux#?}J{3$^g~g*9iFr}VhtpIcb+i9bfgzH$iYb=7@e-{D7&BZRmJv?dKoXom;F--WLqHllCs zvoLyg8n!!d?kUPH8N9Qg#x)F{U0C1wdU!r{FJj91hE4SL1YlOAa_1ZDS0AdJIhy5) z!u_-4fwaP(3Wl6F>yh&%?iPRG!$U4CVG8D0F=3D_j^-;1USn~~;!`lJ2-ZzAty6m- zD0MsCJTGPzockQpn$0z^EigfB&imSI7RcyaE@I~B+?UH+D};{xb)rbVNa-A zCU&5T^U z6FO0GV$JbnfpuPOp5Aj&%CA<5e*EN30fTYkggbvjOmz|?gt-#ZY-ld@h+tK{`s;!0 zX;9PfwAQLw&B!cg&qM73pX>HQ$vF8U8)8R#sNhJ#RH4dA9yH{ar zO4F~(uDKaIE@quW*OMo`+ky0xVQhv*DY;*6B+Cy$tMODp$<}4` z2C9T&J0F#=TYVh}ykC@qR)yExmCuU&H-Y2qwBSX!KZAQigLaAQC^5X7NHs1aZe6PA zU_T|x4-K-iOCV(t=1mi76dADARn&=C%9l(&#Yg`Ykxjw=-0om#2F1s&n>mBIgqNVS zoSwmb>Zwws>_;a)D(I|)lzbtLb0e#wLKP22=`xp-#ngmbPZN?AC*kozGC$Osp7u3& za~(Ys?>AChx0-1`dl)marf0cnkDu5TlEphUJp=VlIKHV~H?izwQ3k%}Wt=z$(rEK= z0Y=}q)}hz z$D@&IqS<~@aEc2)QM{`}m=!0U>Pe>8~Uhoyy#9=k*S-Iqk(%q|hW@bc%mo z_3m;@PPi8xSF#?8N)I zw@@c9m5fSW#!N+q)P#{0qKM(+*93c}Yt(3sfzY=olg#;)cos>7x!s31Lp?2GOzY4W zB9Edn>Hcp-UidR2^O*H`O_~Q%t~ki8zM0EPA%+XiP~zjV_F?yfc9Qcl9tSF)T^`+r z{73?_KT|NImuGp(yL+3<`~~5)u=wqQZjt^0^6{5bil3un?aNnJ2xk0>s9e&hA7!fU$t(B-(vS+mp@k1XN+H> zSLoQz?O$4eCbSGaU%~d~lge6;I=efD;~;PNm*lx9+!aX7IY|AwQtgyDv)*+_bY6d)juxt6q+#0rxi*|&jwxlW z9WqeEx^$#k)~(u%d_J}7muokI;M(Rcn`M=gFDsG^pP`R6RxdXguDHMB;&>5zTelqQ zM+ub$KEo=N_BE`s?0eU1Io;2=fY!Q`X!>;0geB(JO`FtANi> zjQQea6VyfJY`ajThoN7DMjsgIWk1B7%Jns;I|A*!F1wxft~Yx9tnbG=9J0&@1e)ty z)qe+Q)U{vn*_F-e_#tqUziNI9(G8(|C~>9wHE71>`4=ye-2n%M{ZeL$^pGS4w(-2X z+M3uIy9u~G&Oq^I&{S2%uYza7^;O=!H4-6;lILV^+rJ(;4C!dSB6j|aWWw5cyPVVT zcce&yDNi5ku#Y@Qpx%-ge}|lU+KsT`PbQj&33-=;Me&g=mCUTMH!B$Z@m64pv5_R~ zw?f%-D8ar|3jL<%?|7TQ)M=?E2On3Vy1OS#U!cfcPJ;sBLQfAa7M^{r{63DbL7bnH z*-^Ck@?)_GwOlGajW-8(8g-;of&S)ME>Ma+-7P%injw;1o^SLu*P?bH4D>L&=NDmBJ+RTb3Ix^-}VyZ65kFYO>)!5rb)1eP`-D zuoYY^eVv;8XHnb1vBMcREuf|J_;o5;yJMYh>kTDh)?e9)1miH9{TVM#(v`4)HKqo} zPL}N$BGO~b!4ZuJIX~O>r{olHr`BO_u#rMD^1CG|n=5B3SKJ$IBr{+7jE7}`cNIkO z{zQ%0*E60#XUnstNK2EU2H#CwGnI6|E%sL zivPKx)by8jDUU@p*B6A0sbQ`mzcGqNKZdY@^wBr-BD@#7F}WcvoPc|Q;^%g4w*{)H zWr{~NQ`QMHc0Q{#mr&4C#SBru1vIpa8wjtI_Q1Gz`ZiOAYEr^ncfpuV4;P7kNIX zFNXWDoQ1u(d+Dn_9}^w`tKTJn7+HWqiGH<$Z^=dY_y?m8E$I=Qb;pD?$uSiJ4fCCh z;O=`gY#GD;oJhCMCRi1y%p{@0Ta#zMc?sN2`seYdUkK4rla0!}FrHA%Yg`x_(Ip>6 z5So_uY_Nu;9Spf?d$%jT9l{-vsVxlBK3ZOR>}Z26uE zLyw~U|0}e_W!xsCq})pfj0d*fuQLInhAM=}zTdqjZuu=YZ(_7X(9fX{F^iz9bG+Hp zaM*duh%st1))^1CrN#wa?*FUoEaReT+pa$|jD(`VfOI!VgCHO!ASEG6Nh2YGv~&v6 zNOwsK(t~t^G>U+P(%nPnF!K!RrFh-<^FF`#D__`i&fe=>$NH~x_A-&2hZH*DI=o)@ z-f{(HsCJIA_g@>u74euG(Sf6=$lV*Vf;yM)`(jYEh{8qhslV)#N!0t`o-id3sxl$I z{U!g8P}WxV8e?qP@8ON|4Iebnugbb0NhR&Z=MQlu@oXJF3)9%AKm0(<4dANzi)fk= zH9bw|YKMxoI!2k7fKv{;V?CCR>HnP4xDl79w7KQT%93==SlTDeq0&-tntTaSO&x$s z7HVEVt;Pyi_6hylM9lRUf%;!kmNZ4R4)orv0qWyrsl@gOkN*l{1B0zvsoL0Kb@44u zglr`r1JQ)vq3=9d=#*Bdd{FQpx8EJqNZluja3ycp(9s3848=XsmaTo!^5wFjiP{TH}*0KpT zP&RE2H{vi~lD@$p(KCvf>V)=VVbRUW@T9(nO5bl+4k_(UchG9 z9f9vkyEJQeLpc5lI~UeVPO*L{Rtzc7Rg1Q`7eB)ZHb(Tszr7kMjP)LETZy#6`STwY zw;TU&V-fe8j;6~qkwX8M3-V(QF))@c>qOJ8o-W##xm~*JBAY{Z2OwGC9!cQ;N5cP? z!6&o&_4yUm@3{p*-8}X_cR2nPOB#VEjKusD2aCltMe}MZ+beD*3FSVE@(-de{(G#6 zkcZ^#eD)brJl4MHcM|Ko)qEp(oSxuP*oYUxDKESENLAONPyfe$_BI={^w}ft7DDbf zN2ge4!Y6l)qhod^O@@?Zn4141QKaPvY?%naB|A@P;Ar?@LiLsUPOX2=QP|((i~pbT zg;@|33JP?5O;{z{*7!PC;6r@M8|lZg6y8(KaH&PiX$3tFL!>lzwamGz{Y%(qlfM-F z1?)BU*3JJIQ^cyPr0g|7@-xifzyYba62%jQW5K6(JGHM_1e<{Y)AIoGofJc_f!~QB zYet5PAIfK$5K)lYa$Sl9b(OxzU5W%1u3*kRmel_-heXj)Uq9twoLI!;Z+maSE_j7b z1$EW);+tvP@k!e)&B}Tb==LoOo`VAA+{mg{#0OQ9`0>(h@#hXhRu>{0Fie63eE~Il z%Ju9%6kL+HOFC}xQ&tFoK^4I}%Lj2~S9y6A37{_O9G%(b%-A!X3{|XVU$3eg5gWn=9K#!oRz?C=X!n5&D zi;U-AldE*_+AZv}4$vV9fArSr4xm%ezS=`q)U+bLl|fa817DJ#VQ!8WjYP9>Z_g1c zb;y;=%Fyde`d=b0oglr+73BPLXA{N8Oi;NM*&__++DjHy2-!7TVc8JsLRwW&bX#iLH}1!T%bJBJf=dP%}njKD&~ zV~pPqDAJI8sIXA>UIRn!y!X4T8_n7Y<6-*g{0QS|1rFoPkY+x$#{X{#2V>!0H9FP@ ztfXbfPv2R~uN}Kyl^Ix4Mea>2ADZzFWhQiEna2GRWu!vd6KbSAY2AjEL$i~e%e7k( z7JHkHS_SNN(HG85@WEz!egXEiyUCdGJ$Jt4G5A9$pu{7$)U;444xwvyejT{QX|o1I zg?6{TwsX!sd61c#&^pH)a#_OTY1tII{DdN#T0W2GEbc+C6F`^YTCRvq)cebLo+?$% zSbRM8(&_mfh(YM7X*|t`zh`eY3<(@=;Xw~KI3?IIbaJdP44UoPBe7F=I(=j5qUriH zNGc+H*W!z59Wm9&)JN`ovVr1b;nJ%!&gIL-FbhqIz@J z5$M~L*`fdZ$G9V(+UzM={V;U0p&QSnE@$#XPVu(*s+qS>Pl3bkEc>>77OPkaxj7;4 z7kBU6SGbGEACk8heWb3k_#9ad;6~J;y9G~or|H1|K7;t#6g80 zAmbimnx`zS4~18}h8^Q_bA7X7Nm48^I5qwy;6HMy4Mup;)mFItT_*#K3v{48Tk@hU zd&dNQxl3%AsO30jLV{xNXMoSF1v(g1f5DiW{-w}A{@JHbI^EBOWP=kWdYn|s7ynB= zrrlsmGBC{ZlV1GPxAO4kxY+WuX9@X#%Xh+~jNY@W=ob%W3@ebfq6>GC^O_~>A~gqj zP((Iw2Ls$Q?fhU2objbdTJ(u5PZ>Ye3g5p$4|~!E5HhUJ?P7^36kXI1*31+nQzU`} z8kLNr4A6iKh!96Mz(mVc{V+&Wp7=(*q2?VxF;Op07zq)5zbeblT@dQ!YULqYlp(%l ze@ZGVcEL(Ke@ZGktj6!?%bx{x{j!2YJ_2qCO+$9ilvafEryD z6psyNm`bR9YYgnfR>{Wwnl5B}PL~a8IO{XKk~?{EQ|nULT{7QqTyVqhA11}9J1a5% zlLcHv>^ZV;DdR@ZKOSqC8J*OzqM@tvkPhDP=0%)*3J!NR-*cgT!Yra`2G7!-BI*i? zoS-vA{t<7~HyRR0{_U=dJJ6Iefs=8-TveHG;};Z80vkJ*A96lLKzTn$ks>K4nTN8V zM$V8D|9D|O!-5^iGH+>u1QXAr7=JdLIT6dU7MxvyY&%8k_Wu7;|7ao}@kBl7*tbAu z?9cGq3fYoVJwh=ZHDepCUvD-(?-Bwq{}a=GPdkoH!=_0TgKv%X?DOBpJ^ zF(~cnCrdRLVGD4+=ALp{w4J2{X2WOWjwX9&gs;Vo8nydSJoa_|e=rc0D6mk%4!O9~ z31c^inO&7~eF_@0=)z+a3is4&S8{ci;VsZrCPoa^84! zW>&!32Kvm{nqz>dh+z>j^~$$9y?-$`3^0Lou`MxHw(jO_!t*n+Y2)Yos{p_H^%LqD<1c^R1OaDq>+9c z0nU8g78+4c!_Dr4NQlJ~e~OT`gWHd( zBHm1uwCr269DfddUTl$(bgWpxLDLLVWr({0=skXE^>{HLny%l7F$rhjF>WS#6Dr(z zp6F7;GWIgx!BJ!5QXxQ`JU)bB1IZ2e_K&`bfCi_wqG)=~fM;~mrfE_yH(RPd)aA$= zOp+!#d9j@<>(Jd_-#(7U_g}(aQ3Z+Omi>qS_7l70zkkC1njx(5i|c<2mVe`2v0m3@ zAc9>a+xOhnZ;4uF&^b0G)ArGd$Z1CpaOSs$w+J)Br3nw2=3Wigcwm-&`8>WpsI7hY z@cOGGF1FQht9J{1Hgm_jeCdYlx-30uS8ZaxK)Ub!M9iaL&EhTvj2wxn!paA?qRdP@ zZ*}rXOHr%e09;}Pk8zLQ=}T!&L3;n91>dV0gZhh$jCRLx9T>IU$b6Keyf`*bI+wQc zGvy65@i4P>r=MbT9thSm2=eX?ma=;6zxL3-QDy_s;f$jq<>in~%Hc|oSwT&L7fAl5 zEtRhoj=iSOn-R%K6M_tbIBulFGchws9ftA8-nd#7;Gg67ek>rX|CIo*8`2b;Qk?aB zHVv||b>rkE!(}m#;LiA|=31C%MWtRv&P2bVyDqaYK*a0`#B4WfzJtKqK5#=CMc#u| zDciFm8!lqb7;!AT86if!b-c;62MknsBw=9}W}E@Mw^Bepn;hi`_$yIaZ4sKMn%r#^ zR|;LA#5ya%uG{eFE?*jD&b`UKF#KTN6FZ1oajWikloITntb7n$v3>+eys6iUNXms- z;a%6_S40zkYfJ$kI{f@kpO2r-bRejYAcv;c*BM9Ka@!YvJzr;t3}sf{kXa`_W_h^` zYmYp!7!Qw7{P}G@#?+*(P%vA+9ntXps9qbwOGcTus<@exIfFYqA1KYLg#lQ6df$yj z=iW>&y`RWp;5Q-|HE)E^Ma#dK%eh1R zr(I{`wRnB$AoU8VbVr(EsI6~)z*5mgNaIWe?2uqAWaQTuPU$WBSb17}l$wPRtUSXE zQRo5wI~g~8!?!^dyfZCRT6OY=dfO0a+TO98$s=BB@>RKUkcTea{<7QWkfISZd*%#p3!QOwUi2&|TukNCC9 z1}a#%16MO;auoFm(H+%jzi!vRIl@_C7kqBYFbk*-vZ(nLqM$5LJ{Rg8^Ien&zQkTT zQm9+xj{z6@j%;w^r(z6?;biX&v;0Xme5IO;vaeR9^HIyVb~^9ed3WJaE+;3e#I=Nrw?Jzj`HTPH=1feEn>6HL z)sFL0=9pmlf&kRE+!h9~2JMYr)jjk#tM8kdRX?qcaT*{;-N!IZZI^oyJPP0u9is-L=^Eu5IvG(6hhJK`(%#1!NeND;3M@&J5Q~k|V=i`ZLPffq_<1Zu|EoGO}(s8~0 zTeFE#nr(b#ov^PtRrzoM}X23{I5`p2)dtf<`48{8%G z{j)PI)!;OP%r_WAdUyR@?{N!t5jvX*D0=(FK79PUMWyO8aqo7yq%kM@#9rQ?nmFsj z77obFNlmA(w^wtV7u)k%L13Bwifm8DMHixlZs1C0UbbS5=)!jJZgag~|9IPDUUwl4 z-+;zrL7A;fPCSBg^&eC_t!;>W0*d=5KrzD(2`}Df7~GGG`=o==(ySfgYP`OrFDFH< zeVX*%|6(v924dwJ3Gklhec1P)GJWCdc&L`7JuUGZuCSrrkfVg-7%2p(zv9^loukwH z=AITv_-ZE9%c1uSB*=9rtEfxjIDQso(%_7uHz2tf>LX=^41L8wzLY9c;y~~}|K)``lvcq#R9f_#E3uofK6tdV<`=5t* zath{|*ID~g9mf?%xk1tW)N90O7TR`-zrrpM?9`L9uUF6y>Huc6g@~MZ{Un)uwa7uGJwV{>~+JpYnyG(HTh4SC5S;en}&G7 zYk_OXSGQZ}@mYVN%8w=JbWpQe9aXjJq3@vD2 zbbwBU4+90!{$iq6G#u~|ns?O}(a4(Atn(C{(n6O2!&{@R z_ov;b#AByz{sl_kwriW9&Z+$aO6gH_WIow58^3p%^4*@QN%GYZLqs&pr#jn&g=4X&ecuIhVR>#8mF`eW4+&S|R*PCzn9 zEY*23r7;~<@28{+7Wjl*ZO>*dShB_3Ut332wosDsuX%xN@1hg+gj@Vq;6u$^CItZC z(8x(iJg(#02nV`E^5HJpCs~}Y`FFN9PoaKV=|d=$+)`6q|Mlz7=4q(;SbQM#D%3OA zK_yQIs=5Zt+hnb>p@&U#p5#T~1GQN61n<>Ho)ocE^2ABXZxyhP#oIf?FpLCdt<`Xs zH0Em1gk_oF2dk9_%d}DI^d63fx0`Cs!}^!487g}h#u@-f%Fx1%Yv++AqbP+}Apo(d zULE9s#UQ);2PNvXE5E~2PBM^|ZP6FoNbgaSKdX;k-22fR-r5U!GcM4RO;n}v^?A_5 z1GTBk!M=EBhMP-th==QV4aU(aG)dh}5T@U~jngfj39L}qwZ#KAh+Ql$sx!hyQKkeU z1)S3PdUg1m`|FQx7q$2Gl}y-Ber>*|FF!*8cHIQ{nFNsx?j^T*yA8uAGHIQAyJW|_ zE@16Tnp;K9cwAcnvZL>uF_P6?eUNIqDsAm#n9m3zRYev`epC{I#g%wmBiW~C9&wE- z$022mEDl`;6VG3njAiQ)Rdn9)u2^ZuN#|;LBCKI|Qeinb(mNd(wNUj>a`gU_YFMDX z*T*O~u)UFP$2CYe&rnqh{N{M^%^J?G5t`(Sf09YlOnX?$*N#I{uEEvXYaa)bp#2`W zTKCc}z4?oSB}l{((EJu~NC(+Ee;Y_nr%fL;g?%;ZjE{(uqg z+aVQd672_!yk5JnCQ8}{5TV2hQin(MY$pj53Icr8RJSjNS1^dD@BZNZ-6!VFC59oe zY)(=c}`7;Vq@y>8PJl4$#B=eW#_~!Y=|%1|gFS-|li~!GxCFuc*%;K@@(UoorLULQ&F8Q(CR&-8RC6V2%sU*0jKt4+kD z%~=i`VLGnP^?G_&J9=8_v4E%$%Bd5EMRXi35kA}#4YIr6AVY)@a3UPH8$T((DIBCL z{)_)w`CJvK)Y;H^JoOg$?J7I_>i9UuxmH-bCfLh@!PwkqImfU!gl?uamT<6*gkX{H z5rfKe@DyAh6No}pCGp6}>w|)|g)Ff17v(~G4GVo6_NS5}0~&ak36#CZ5qzUu0XawN z`?$rAx1X$1aQrF;O*~EAZy&>xO>>?(07Gu!>m4_B&w*H)J?75OgNQeGQO#}QEmTv! zV#{mR5Dnp9QRM}rfxABXG>y<$9$qimyMFY|wqo~a$N4jb_X}Jm0pn()bxZI;gvYCoA| zNM#ieSn5=TSrgJVP4qH(Zi#(AexUVo>3S6+C?!u0i#$X;y=lD$trWnI*|41N_QHH0 zc4Ebi;AvcpJJL1{$Cbm-#lbtp z@#P}yFBjELHd25jM6q;9G2-r66$-jjOfOx9#hW5-42S?4VcNA{N~~&cJ)AtQ&^8Ep zK0^*Zkw*#q8}HTpXOJU4kvfDL&vEK-pNL4q7UuHn2k)Zi{~^xQ=;CCKz)UO~G0~U; z(c?`0)S5Qon3Hn$1tRFje(bw6$?pYT3_k7YW&DbZN+5C+#lI;&9Qa^uWi=$hg-mNX zij=wSsbLeOKUq!BeG8hn#K+g|g1Sl=*8Y4jr7*zjQ=2A|M$&oibGlm{(_1|Rllrg2 zHH3ZGt**S?F|xC28=oq0gois)+mh- zEO=fYRja#jd@V#uETNTNwvAVenk)1wKQu^C`|G<}vSsKrGWe9=s*CJ>zF0zPffyp)E1`yqU>Bo5%^EIBEw7;**#>3juuFpm zKec@QXeL#YoY>Yomeh2e1@1$Xpx8ZvRx*^bB+2LD6#stO4#i0Zs6gknm8fmQ4KaLe zhkOj7Njj;W4H=bts&mJmFe7ECt1nz0^9X~n{o7-qI)yewCv~nqMI=C%Hf(9bf3ln8 zx^{!qIHzPQD{&)<)F|&4MV{i?`y6oJVFdwwai*B7*otTp-n$QM+A_oCm681Bk4u=w zk`p$9+XLaX?S<#cxdk}z?$;+dlf5{8_mcu3D_Fq%_acIulgrh?8&$WhcD!M7PwOCr zO%WYV$@N~6pDZ1ci9L>Rb|A#z-1B-onsF=TPD3djy4m;$aAYn5mwHtq$IMtyxz7@w zBLRR5i{RTU^Q^Ngi#2>zLYVI;8-At+&@fdIvG+BQ~6_sCEEZH^HTX1MKYWf#VaJ59wz2lcotm>h( z1J^e12gf{T2rS+gOn?Psad546B%eb`(^*QkJfq|!&p@(6sE&@4skAMCzP$&6hq=l} zV4Mz{nsCE9=yh7dIy&Re_nv}8xOOhAO~3A%-ycz*9-wmHmY+UFwsPqkSRmDn5dnA5 z*0YI@3O00&#c!CCE`ixFH>q8fGVeQ)`Fa6g+go<&Uj^-XaBjGRXR8G!<_jhdLJWea z^KRHbe*)KpFbAXI_C~!QZ1eZ#8>kd!6TVfKZaUhuR`urW1#RlsO0oe%TWwBYla-IP z>@81Tl0vjd?QCf#h*4J)v7Zw9p}ZWGS?SsMnze$*36l|m@gAIcdbQ|ax=S=wNO-Ta z9C`%H&z?lJ znD~^TyTpG@?|!>eh>x>oWswoRqeAvu>Cm>aH0L0qK*K!tMTWs4>Z85z@|te9b#RNF zpkv*4gO$VPCC`oiuEOp2ykt@KJWcGvRi2#e{!{kA9P<@C6%2!w$w%xDsW7Xj8DvvG zAYK}x~oY{$2dKh>rrc^DPoP|wfU$+y}kj-AcPeu@TY;9aD``8RoLJW6{HVbM= zpDfo-xl1{5@7b38Of@*z?v)Tgy5-$He_9|3|FZ{u&)gZcQfqkCCT((vgE)_|>>HJr zAl$6e#+66%HC}Cg4Mu$57CiJqA4r5MQD?OpSuB*04-=38&E8R%nQf!ZZp8&|foN_C zHa=$;YaN-A$1K?&YPsv>l>20B5gE7Xh6wGG%!lG2A}gFY-|=170IdPAM}qEbSb2$1HYsJ=WoaRF^{}nq6TNYnVjJ*%o z6EyeNfZkqo-`|7nZoFD=xPjx`z{UW4G55HE!qoEjlkxFt!{Q>+Z1gJo5B82nrUL86-vs__?_oHzTq}+5FtG88B_;*Y!iH%L0k&M-%~W2 zXk=G6Puw^5VpV!|<`Jgfi(olHmU-D`a?h}G)cT3Rm7glX%g2##pTQfqM&FArM-~xx6&Z0= zh2)%NxiftM*K?qM75SvD>=&A1FOKp%PZp|^(`Vu&J?*pfK0mLbUGBH=*=ksrO@S~q zX)VBrDAtUdlpUtn&rL<{0veZYh1k3W7oNnf>(Y^Xo&oBDFCEy48oWtuE zm6aPlc+m2*M}xkK7Z1$sI@=cowL4}(iPxv)g6tzw5>I5Bg!Y;sU&Em1#WtSLj$y)NQp+`NUQ!T4`XtUu4ie;F=F@+#V7L o03eax57~48008>Mu#5(fDTq}Fa=cB3`U^l#T1l$-p24gC0W?WL{r~^~ literal 35949 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

(|V7O%OOlsc*vU+P^72X!U7K_eX*CG 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 E`T9pxN5i1O>p>VAy6%lIREYyb! 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 Kl11VsAO0U5dJ04U diff --git a/src/main/config/env.ts b/src/main/config/api.ts similarity index 98% rename from src/main/config/env.ts rename to src/main/config/api.ts index 8f1ec73..7c15de3 100644 --- a/src/main/config/env.ts +++ b/src/main/config/api.ts @@ -1,7 +1,7 @@ // Point d'accès unique aux variables d'environnement dans le main process. // Avec electron-vite, les variables VITE_* sont injectées via import.meta.env. -import type { Env } from '../../renderer/store/env' +import type { Env } from '@shared/types/env' /** * Retourne l'URL de base de l'API pour un env donné. diff --git a/src/main/icon.ts b/src/main/icon.ts new file mode 100644 index 0000000..e4c6a72 --- /dev/null +++ b/src/main/icon.ts @@ -0,0 +1,63 @@ +import { app, nativeImage } from 'electron' +import { existsSync } from 'fs' +import { join } from 'path' +import { is } from '@electron-toolkit/utils' + +/** Nom du fichier icône selon la plateforme. */ +function iconFileName(): string { + if (process.platform === 'win32') return 'icon.ico' + if (process.platform === 'darwin') return 'icon.icns' + return 'icon.png' +} + +/** Chemins candidats (dev electron-vite, preview, build packagé). */ +function candidateIconPaths(): string[] { + const name = iconFileName() + const paths: string[] = [] + + // Dev / preview : `out/main` → `../../resources` + paths.push(join(__dirname, '../../resources', name)) + + // Racine du projet (app.getAppPath() en dev) + try { + paths.push(join(app.getAppPath(), 'resources', name)) + } catch { + /* app pas encore prêt */ + } + + // Binaire packagé (extraResources) + if (process.resourcesPath) { + paths.push(join(process.resourcesPath, name)) + } + + return paths +} + +/** Chemin absolu vers l’icône du launcher, ou null si introuvable. */ +export function resolveAppIconPath(): string | null { + for (const p of candidateIconPaths()) { + if (existsSync(p)) return p + } + return null +} + +export function loadAppIcon(): Electron.NativeImage | undefined { + const iconPath = resolveAppIconPath() + if (!iconPath) { + if (is.dev) { + console.warn('[Icon] Fichier icône introuvable — barre des tâches Electron par défaut.') + } + return undefined + } + const image = nativeImage.createFromPath(iconPath) + return image.isEmpty() ? undefined : image +} + +/** Icône dock macOS + cohérence globale. */ +export function applyAppIcon(): void { + const image = loadAppIcon() + if (!image) return + if (process.platform === 'darwin' && app.dock) { + app.dock.setIcon(image) + } +} diff --git a/src/main/index.ts b/src/main/index.ts index 4333634..806b0aa 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,7 +4,7 @@ import path from 'path' import fs from 'fs' import { execSync } from 'child_process' import { electronApp, optimizer, is } from '@electron-toolkit/utils' -import icon from '../../resources/icon.png?asset' +import { applyAppIcon, loadAppIcon } from './icon' import { registerFilesHandlers } from './services/game' import { registerVersionHandlers } from './services/version' import { registerGameStatusHandlers } from './services/gameStatus' @@ -101,6 +101,8 @@ if (!gotLock) { } function createWindow(): void { + const appIcon = loadAppIcon() + mainWindow = new BrowserWindow({ width: parsePositiveInt(import.meta.env.VITE_WINDOW_WIDTH, 1200), height: parsePositiveInt(import.meta.env.VITE_WINDOW_HEIGHT, 800), @@ -111,7 +113,7 @@ function createWindow(): void { title: app.getName(), show: false, autoHideMenuBar: true, - ...(process.platform === 'linux' ? { icon } : {}), + ...(appIcon ? { icon: appIcon } : {}), webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false @@ -142,7 +144,9 @@ function createWindow(): void { } app.whenReady().then(() => { - electronApp.setAppUserModelId('com.electron') + electronApp.setAppUserModelId('com.dyingstar.launcher') + + applyAppIcon() registerLinuxProtocol() @@ -150,8 +154,6 @@ app.whenReady().then(() => { optimizer.watchWindowShortcuts(window) }) - ipcMain.on('ping', () => console.log('pong')) - ipcMain.removeHandler('app:quit') ipcMain.handle('app:quit', () => { app.quit() diff --git a/src/main/services/auth.ts b/src/main/services/auth.ts index b4ce8da..a6cf546 100644 --- a/src/main/services/auth.ts +++ b/src/main/services/auth.ts @@ -3,8 +3,11 @@ import crypto from 'crypto' import fs from 'fs' import path from 'path' import { URL } from 'url' -import { getApiBase, ENDPOINTS } from '../config/env' -import type { Env } from '../../renderer/store/env' +import { getApiBase, ENDPOINTS } from '../config/api' +import type { Env } from '@shared/types/env' +import type { UserInfo } from '@shared/types/auth' + +export type { UserInfo } from '@shared/types/auth' // ─── Types ──────────────────────────────────────────────────────────────────── @@ -16,12 +19,6 @@ interface TokenSet { token_type: string } -export interface UserInfo { - sub: string - username: string - email: string -} - interface PKCESession { env: Env verifier: string diff --git a/src/main/services/downloader.ts b/src/main/services/downloader.ts index 34348ea..f32eca5 100644 --- a/src/main/services/downloader.ts +++ b/src/main/services/downloader.ts @@ -8,9 +8,9 @@ import { getUniverseZipUrlForPlatform, getTestingZipUrlForPlatform, ENDPOINTS -} from '../config/env' +} from '../config/api' import { fetchRemoteGameVersion } from './version' -import type { Env } from '../../renderer/store/env' +import type { Env } from '@shared/types/env' /** Sous-dossier d’installation du payload du jeu (ZIP extrait ici, pas à la racine du chemin choisi). */ export const GAME_INSTALL_SUBDIR = 'DyingStar' diff --git a/src/main/services/game.ts b/src/main/services/game.ts index e53fd4d..2619c4f 100644 --- a/src/main/services/game.ts +++ b/src/main/services/game.ts @@ -5,7 +5,7 @@ import * as fs from 'fs' import { downloadAndInstall, GAME_INSTALL_SUBDIR, resolveInstalledVersion } from './downloader' import { loadFreshGameToken } from './auth' import { clearDyingStarGodotCaches } from './godotUserdataCache' -import type { Env } from '../../renderer/store/env' +import type { Env } from '@shared/types/env' // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/src/main/services/gameStatus.ts b/src/main/services/gameStatus.ts index 4eaa342..fab2d94 100644 --- a/src/main/services/gameStatus.ts +++ b/src/main/services/gameStatus.ts @@ -1,17 +1,9 @@ import { ipcMain } from 'electron' -import { getApiBase, ENDPOINTS, getStatusComponentId, getStatusMetricId } from '../config/env' -import type { Env } from '../../renderer/store/env' +import { getApiBase, ENDPOINTS, getStatusComponentId, getStatusMetricId } from '../config/api' +import type { Env } from '@shared/types/env' +import type { GameStatusResult, ServerStatusValue } from '@shared/types/game' -// ─── Types ──────────────────────────────────────────────────────────────────── - -export type ServerStatusValue = 'online' | 'degraded' | 'offline' | 'maintenance' | 'unknown' | 'unavailable' - -export interface GameStatusResult { - status: ServerStatusValue - statusLabel: string // label brut de l'API (pour debug/log) - players: number - available: boolean // false si l'env n'est pas configuré -} +export type { GameStatusResult, ServerStatusValue } from '@shared/types/game' // Mapping value → type interne // 1: Operational, 2: Performance Issues, 3: Partial Outage, diff --git a/src/main/services/version.ts b/src/main/services/version.ts index b7c6145..65c2398 100644 --- a/src/main/services/version.ts +++ b/src/main/services/version.ts @@ -1,37 +1,25 @@ import { ipcMain, app } from 'electron' -import { getApiBase, ENDPOINTS } from '../config/env' -import type { Env } from '../../renderer/store/env' +import { getApiBase, ENDPOINTS } from '../config/api' +import type { Env } from '@shared/types/env' +import type { GameVersionInfo, VersionCheckResult } from '@shared/types/version' -// ─── Types ──────────────────────────────────────────────────────────────────── +// ─── Types internes (API distantes) ─────────────────────────────────────────── interface RemoteVersionPayload { - version: string // YYYYMMDDHHMMSS ex: "20260510092919" - releaseDate: string // YYYY-MM-DD ex: "2026-05-10" + version: string + releaseDate: string } interface GitHubLatestReleasePayload { - tag_name: string + tag_name: string published_at?: string } -interface GameVersionInfo { - version: string | null - releaseDate: string | null -} - interface LauncherReleaseInfo { - version: string + version: string releaseDate: string | null } -interface VersionCheckResult { - currentLauncherVersion: string - latestLauncherVersion: string | null - latestLauncherReleaseDate: string | null - launcherUpdateAvailable: boolean - latestGameVersions: Record -} - // ─── Helpers ────────────────────────────────────────────────────────────────── /** Compare semver / tags du launcher (ex. 0.2.0 vs 0.10.1). */ diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 41f4e3f..ada397d 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,118 +1,54 @@ import { ElectronAPI } from '@electron-toolkit/preload' -import type { Env } from '../renderer/store/env' - -type ServerStatusValue = 'online' | 'degraded' | 'offline' | 'maintenance' | 'unknown' | 'unavailable' +import type { Env } from '@shared/types/env' +import type { UserInfo } from '@shared/types/auth' +import type { InstallResult } from '@shared/types/install' +import type { GameVersionInfo, VersionCheckResult } from '@shared/types/version' +import type { ServerStatusValue } from '@shared/types/game' interface ServerStatusResult { - status: ServerStatusValue - players: number + status: ServerStatusValue + players: number available: boolean } -interface InstallResult { - version: string - releaseDate: string -} - -// Ajout du type manquant (référencé dans VersionCheckResult) -interface GameVersionInfo { - version: string | null - releaseDate: string | null -} - -interface VersionCheckResult { - currentLauncherVersion: string - latestLauncherVersion: string | null - latestLauncherReleaseDate: string | null - launcherUpdateAvailable: boolean - latestGameVersions: Record -} - -interface UserInfo { - sub: string - username: string - email: string -} - type AuthStateChangedPayload = | { env: Env; status: 'connected'; user: UserInfo } - | { env: Env; status: 'error'; error: string } + | { env: Env; status: 'error'; error: string } declare global { interface Window { electron: ElectronAPI api: { - // ── Fichiers / Installation ────────────────────────────────────────── - - /** Ouvre un dialogue natif de sélection de répertoire. */ selectDirectory: () => Promise - - /** Lance le téléchargement et l'installation du jeu. */ installGame: (env: Env, installPath: string) => Promise - - /** S'abonne aux événements de progression de l'installation (0–100). */ onInstallProgress: (callback: (progress: number, label: string) => void) => void - - /** Lit CHANGELOG.md dans le dossier d’installation (après extraction du ZIP). */ readChangelog: (installPath: string) => Promise - - /** Relit la version installée (API /version + version.json). */ resolveInstalledVersion: ( env: Env, installPath: string ) => Promise - - /** Supprime shader_cache et chunk_cache dans le userdata Godot DyingStar. */ clearGodotGameCache: () => Promise<{ root: string removed: string[] skipped: string[] errors: { path: string; message: string }[] }> - - // ── Jeu ───────────────────────────────────────────────────────────── - - /** Lance l'exécutable du jeu (détaché du launcher). */ launchGame: (env: Env, installPath: string) => Promise - - /** True si un processus jeu lancé par le launcher est encore actif. */ isGameRunning: () => Promise - - /** Écoute les changements d'état « jeu en cours ». */ onGameRunningChanged: (callback: (running: boolean) => void) => void - - /** Récupère le statut du serveur et le nombre de joueurs connectés. */ getServerStatus: (env: Env) => Promise - - // ── Disponibilité ───────────────────────────────────────────────────── - checkEnvAvailability: () => Promise> - - /** Ferme complètement le launcher. */ + checkEnvAvailability: () => Promise> quitApp: () => Promise - minimizeWindow: () => Promise - closeWindow: () => Promise - fitWindowToContent: (size: { width: number; height: number }) => Promise - - // ── Versions ───────────────────────────────────────────────────────── - checkVersions: () => Promise - - // ── Auth ───────────────────────────────────────────────────────────── - - /** Ouvre le navigateur sur la page Discord/Keycloak pour l'env donné. */ authLogin: (env: Env) => Promise - - /** Efface les tokens de l'env donné et ouvre la page de déconnexion Keycloak. */ authLogout: (env: Env) => Promise - - /** Recharge la session depuis le stockage chiffré pour l'env donné. */ authLoadUser: (env: Env) => Promise - - /** S'abonne aux changements d'état auth — payload inclut l'env concerné. */ onAuthStateChanged: (callback: (data: AuthStateChangedPayload) => void) => void } } -} \ No newline at end of file +} + +export {} diff --git a/src/preload/index.ts b/src/preload/index.ts index 51bbcd3..9788b44 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,8 +1,8 @@ import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' -import type { Env } from '../renderer/store/env' -import type { UserInfo } from '../main/services/auth' -import type { ServerStatusValue } from '../main/services/gameStatus' +import type { Env } from '@shared/types/env' +import type { UserInfo } from '@shared/types/auth' +import type { ServerStatusValue } from '@shared/types/game' const api = { // ── Fichiers / Installation ──────────────────────────────────────────────── diff --git a/src/renderer/src/app.tsx b/src/renderer/App.tsx similarity index 73% rename from src/renderer/src/app.tsx rename to src/renderer/App.tsx index 51bc16c..4f8d8b0 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/App.tsx @@ -1,7 +1,7 @@ -import Shell from '@components/layout/shell' +import Shell from '@components/layout/Shell' import { useEffect } from 'react' -import { useVersionStore } from '@store/version' -import { useAvailabilityStore } from '@store/availability' +import { useVersionStore } from '@stores/version' +import { useAvailabilityStore } from '@stores/availability' function useAppStartup(): void { const { checkVersions, checked: versionChecked } = useVersionStore() diff --git a/src/renderer/src/assets/fonts/Poppins-Bold.ttf b/src/renderer/assets/fonts/Poppins-Bold.ttf similarity index 100% rename from src/renderer/src/assets/fonts/Poppins-Bold.ttf rename to src/renderer/assets/fonts/Poppins-Bold.ttf diff --git a/src/renderer/src/assets/fonts/Poppins-BoldItalic.ttf b/src/renderer/assets/fonts/Poppins-BoldItalic.ttf similarity index 100% rename from src/renderer/src/assets/fonts/Poppins-BoldItalic.ttf rename to src/renderer/assets/fonts/Poppins-BoldItalic.ttf diff --git a/src/renderer/src/assets/fonts/Poppins-Italic.ttf b/src/renderer/assets/fonts/Poppins-Italic.ttf similarity index 100% rename from src/renderer/src/assets/fonts/Poppins-Italic.ttf rename to src/renderer/assets/fonts/Poppins-Italic.ttf diff --git a/src/renderer/src/assets/fonts/Poppins-Regular.ttf b/src/renderer/assets/fonts/Poppins-Regular.ttf similarity index 100% rename from src/renderer/src/assets/fonts/Poppins-Regular.ttf rename to src/renderer/assets/fonts/Poppins-Regular.ttf diff --git a/src/renderer/src/assets/icons/dyingstar-logo.png b/src/renderer/assets/icons/dyingstar-logo.png similarity index 100% rename from src/renderer/src/assets/icons/dyingstar-logo.png rename to src/renderer/assets/icons/dyingstar-logo.png diff --git a/src/renderer/src/assets/main.css b/src/renderer/assets/main.css similarity index 100% rename from src/renderer/src/assets/main.css rename to src/renderer/assets/main.css diff --git a/src/renderer/components/layout/navbar.tsx b/src/renderer/components/layout/Navbar.tsx similarity index 98% rename from src/renderer/components/layout/navbar.tsx rename to src/renderer/components/layout/Navbar.tsx index 2502ea2..9574e56 100644 --- a/src/renderer/components/layout/navbar.tsx +++ b/src/renderer/components/layout/Navbar.tsx @@ -1,12 +1,12 @@ import type React from 'react' import { useId } from 'react' -import i18n from '@renderer/i18n' -import { useEnvStore } from '@store/env' -import { useNavigationStore } from '@store/navigation' -import type { View } from '@store/navigation' +import i18n from '@i18n' +import { useEnvStore } from '@stores/env' +import { useNavigationStore } from '@stores/navigation' +import type { View } from '@stores/navigation' import { useTranslation } from 'react-i18next' -import Button from '@components/ui/button' -import DiscordIcon from '@components/ui/discordIcon' +import Button from '@components/ui/primitives/Button' +import DiscordIcon from '@components/ui/primitives/icons/DiscordIcon' import type { ReactNode } from 'react' function Icon({ diff --git a/src/renderer/components/layout/shell.tsx b/src/renderer/components/layout/Shell.tsx similarity index 51% rename from src/renderer/components/layout/shell.tsx rename to src/renderer/components/layout/Shell.tsx index c4e19f3..af5e863 100644 --- a/src/renderer/components/layout/shell.tsx +++ b/src/renderer/components/layout/Shell.tsx @@ -1,10 +1,9 @@ -import Navbar from './navbar' -import { useNavigationStore } from '@store/navigation' -import { useFitWindowToContent } from '@renderer/hooks/useFitWindowToContent' -import Universe from '@views/universe' -import UniverseTesting from '@views/universeTesting' -import Social from '@views/social' -import Lore from '@views/lore' +import Navbar from './Navbar' +import { useNavigationStore } from '@stores/navigation' +import { useFitWindowToContent } from '@hooks/useFitWindowToContent' +import UniverseView from '@views/UniverseView' +import Social from '@views/Social' +import Lore from '@views/Lore' export default function Shell(): React.JSX.Element { const { currentView } = useNavigationStore() @@ -12,11 +11,11 @@ export default function Shell(): React.JSX.Element { const renderView = (): React.JSX.Element => { switch (currentView) { - case 'universe': return - case 'universe-testing': return + case 'universe': return + case 'universe-testing': return case 'social': return case 'lore': return - default: return + default: return } } diff --git a/src/renderer/components/panel/accountPanel.tsx b/src/renderer/components/panel/AccountPanel.tsx similarity index 90% rename from src/renderer/components/panel/accountPanel.tsx rename to src/renderer/components/panel/AccountPanel.tsx index beb69f6..523207d 100644 --- a/src/renderer/components/panel/accountPanel.tsx +++ b/src/renderer/components/panel/AccountPanel.tsx @@ -1,9 +1,9 @@ -import { useAccountStore } from '@store/account' -import { useEnvStore } from '@store/env' -import { useAvailabilityStore } from '@store/availability' +import { useAccountStore } from '@stores/account' +import { useEnvStore } from '@stores/env' +import { useAvailabilityStore } from '@stores/availability' import { useTranslation } from 'react-i18next' -import Button from '@components/ui/button' -import DiscordIcon from '@components/ui/discordIcon' +import Button from '@components/ui/primitives/Button' +import DiscordIcon from '@components/ui/primitives/icons/DiscordIcon' export default function AccountPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() diff --git a/src/renderer/components/panel/filesPanel.tsx b/src/renderer/components/panel/FilesPanel.tsx similarity index 92% rename from src/renderer/components/panel/filesPanel.tsx rename to src/renderer/components/panel/FilesPanel.tsx index 549a075..f615e4c 100644 --- a/src/renderer/components/panel/filesPanel.tsx +++ b/src/renderer/components/panel/FilesPanel.tsx @@ -1,16 +1,16 @@ import { useState, useCallback, useEffect } from 'react' -import { useFilesStore } from '@store/files' -import { useEnvStore } from '@store/env' -import { useVersionStore } from '@store/version' -import { useAvailabilityStore } from '@store/availability' -import type { GameVersionInfo } from '@store/version' +import { useFilesStore } from '@stores/files' +import { useEnvStore } from '@stores/env' +import { useVersionStore } from '@stores/version' +import { useAvailabilityStore } from '@stores/availability' +import type { GameVersionInfo } from '@stores/version' import { useTranslation } from 'react-i18next' -import Button from '@components/ui/button' -import InputField from '@components/ui/inputField' -import UpdateAlert from '@components/ui/updateAlert' -import ChangelogModal from '@components/ui/changelogModal' -import { formatReleaseDateDisplay } from '@renderer/lib/formatReleaseDate' -import { isGameUpdateAvailable } from '@renderer/lib/isGameUpdateAvailable' +import Button from '@components/ui/primitives/Button' +import InputField from '@components/ui/primitives/InputField' +import UpdateAlert from '@components/ui/feedback/UpdateAlert' +import ChangelogModal from '@components/ui/overlays/ChangelogModal' +import { formatReleaseDateDisplay } from '@lib/formatReleaseDate' +import { isGameUpdateAvailable } from '@lib/isGameUpdateAvailable' export default function FilesPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() diff --git a/src/renderer/components/panel/gamePanel.tsx b/src/renderer/components/panel/GamePanel.tsx similarity index 89% rename from src/renderer/components/panel/gamePanel.tsx rename to src/renderer/components/panel/GamePanel.tsx index 9965158..944b522 100644 --- a/src/renderer/components/panel/gamePanel.tsx +++ b/src/renderer/components/panel/GamePanel.tsx @@ -1,15 +1,15 @@ import { useEffect } from 'react' -import { useGameStore } from '@store/game' -import { useFilesStore } from '@store/files' -import { useVersionStore } from '@store/version' -import { useEnvStore, type Env } from '@store/env' -import { isGameUpdateAvailable } from '@renderer/lib/isGameUpdateAvailable' -import { useAvailabilityStore } from '@store/availability' -import { useAccountStore } from '@store/account' +import { useGameStore } from '@stores/game' +import { useFilesStore } from '@stores/files' +import { useVersionStore } from '@stores/version' +import { useEnvStore, type Env } from '@stores/env' +import { isGameUpdateAvailable } from '@lib/isGameUpdateAvailable' +import { useAvailabilityStore } from '@stores/availability' +import { useAccountStore } from '@stores/account' import { useTranslation } from 'react-i18next' -import ServerStatus from '@components/ui/serverStatus' -import Button from '@components/ui/button' -import PanelMessage from '@components/ui/panelMessage' +import ServerStatus from '@components/ui/feedback/ServerStatus' +import Button from '@components/ui/primitives/Button' +import PanelMessage from '@components/ui/feedback/PanelMessage' function statusPageUrlForEnv(env: Env): string { const raw = diff --git a/src/renderer/components/panel/socialPanel.tsx b/src/renderer/components/panel/SocialPanel.tsx similarity index 89% rename from src/renderer/components/panel/socialPanel.tsx rename to src/renderer/components/panel/SocialPanel.tsx index db9558a..e29b07f 100644 --- a/src/renderer/components/panel/socialPanel.tsx +++ b/src/renderer/components/panel/SocialPanel.tsx @@ -1,12 +1,12 @@ // src/renderer/components/panel/socialPanel.tsx import { useEffect } from 'react' -import { useSocialStore } from '@store/social' -import { useEnvStore } from '@store/env' -import { useNavigationStore } from '@store/navigation' +import { useSocialStore } from '@stores/social' +import { useEnvStore } from '@stores/env' +import { useNavigationStore } from '@stores/navigation' import { useTranslation } from 'react-i18next' -import FriendItem from '@components/ui/friendItem' -import Button from '@components/ui/button' +import FriendItem from '@components/ui/social/FriendItem' +import Button from '@components/ui/primitives/Button' export default function SocialPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() diff --git a/src/renderer/components/ui/panelMessage.tsx b/src/renderer/components/ui/feedback/PanelMessage.tsx similarity index 100% rename from src/renderer/components/ui/panelMessage.tsx rename to src/renderer/components/ui/feedback/PanelMessage.tsx diff --git a/src/renderer/components/ui/serverStatus.tsx b/src/renderer/components/ui/feedback/ServerStatus.tsx similarity index 94% rename from src/renderer/components/ui/serverStatus.tsx rename to src/renderer/components/ui/feedback/ServerStatus.tsx index fef516b..a12ed64 100644 --- a/src/renderer/components/ui/serverStatus.tsx +++ b/src/renderer/components/ui/feedback/ServerStatus.tsx @@ -1,4 +1,4 @@ -import type { ServerStatusValue } from '@store/game' +import type { ServerStatusValue } from '@stores/game' type Props = { status: ServerStatusValue } diff --git a/src/renderer/components/ui/updateAlert.tsx b/src/renderer/components/ui/feedback/UpdateAlert.tsx similarity index 100% rename from src/renderer/components/ui/updateAlert.tsx rename to src/renderer/components/ui/feedback/UpdateAlert.tsx diff --git a/src/renderer/components/ui/loreArticle.tsx b/src/renderer/components/ui/lore/LoreArticle.tsx similarity index 90% rename from src/renderer/components/ui/loreArticle.tsx rename to src/renderer/components/ui/lore/LoreArticle.tsx index 6f81532..3a10ecf 100644 --- a/src/renderer/components/ui/loreArticle.tsx +++ b/src/renderer/components/ui/lore/LoreArticle.tsx @@ -1,11 +1,11 @@ import { useEffect, useState } from 'react' -import { useLoreStore } from '@store/lore' +import { useLoreStore } from '@stores/lore' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import rehypeRaw from 'rehype-raw' import rehypeSanitize from 'rehype-sanitize' -const loreMarkdownFiles = import.meta.glob('../../src/content/lore/*.md', { +const loreMarkdownFiles = import.meta.glob('../../../content/lore/*.md', { query: '?raw', import: 'default' }) as Record Promise> @@ -17,7 +17,7 @@ export default function LoreArticle() { useEffect(() => { if (!current) return - const key = `../../src/content/lore/${current.file}` + const key = `../../../content/lore/${current.file}` const loader = loreMarkdownFiles[key] if (!loader) { diff --git a/src/renderer/components/ui/loreSidebar.tsx b/src/renderer/components/ui/lore/LoreSidebar.tsx similarity index 96% rename from src/renderer/components/ui/loreSidebar.tsx rename to src/renderer/components/ui/lore/LoreSidebar.tsx index d6c3bae..6574572 100644 --- a/src/renderer/components/ui/loreSidebar.tsx +++ b/src/renderer/components/ui/lore/LoreSidebar.tsx @@ -1,4 +1,4 @@ -import { useLoreStore } from '@store/lore' +import { useLoreStore } from '@stores/lore' export default function LoreSidebar() { const { articles, current, select } = useLoreStore() diff --git a/src/renderer/components/ui/changelogModal.tsx b/src/renderer/components/ui/overlays/ChangelogModal.tsx similarity index 100% rename from src/renderer/components/ui/changelogModal.tsx rename to src/renderer/components/ui/overlays/ChangelogModal.tsx diff --git a/src/renderer/components/ui/button.tsx b/src/renderer/components/ui/primitives/Button.tsx similarity index 100% rename from src/renderer/components/ui/button.tsx rename to src/renderer/components/ui/primitives/Button.tsx diff --git a/src/renderer/components/ui/inputField.tsx b/src/renderer/components/ui/primitives/InputField.tsx similarity index 100% rename from src/renderer/components/ui/inputField.tsx rename to src/renderer/components/ui/primitives/InputField.tsx diff --git a/src/renderer/components/ui/discordIcon.tsx b/src/renderer/components/ui/primitives/icons/DiscordIcon.tsx similarity index 100% rename from src/renderer/components/ui/discordIcon.tsx rename to src/renderer/components/ui/primitives/icons/DiscordIcon.tsx diff --git a/src/renderer/components/ui/friendItem.tsx b/src/renderer/components/ui/social/FriendItem.tsx similarity index 97% rename from src/renderer/components/ui/friendItem.tsx rename to src/renderer/components/ui/social/FriendItem.tsx index d75d1ac..e461969 100644 --- a/src/renderer/components/ui/friendItem.tsx +++ b/src/renderer/components/ui/social/FriendItem.tsx @@ -1,4 +1,4 @@ -import { Friend } from '@store/social' +import { Friend } from '@stores/social' type Props = { friend: Friend diff --git a/src/renderer/components/ui/friendRow.tsx b/src/renderer/components/ui/social/FriendRow.tsx similarity index 100% rename from src/renderer/components/ui/friendRow.tsx rename to src/renderer/components/ui/social/FriendRow.tsx diff --git a/src/renderer/components/ui/orgaRow.tsx b/src/renderer/components/ui/social/OrgaRow.tsx similarity index 99% rename from src/renderer/components/ui/orgaRow.tsx rename to src/renderer/components/ui/social/OrgaRow.tsx index 5456511..5bf881e 100644 --- a/src/renderer/components/ui/orgaRow.tsx +++ b/src/renderer/components/ui/social/OrgaRow.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import type { Orga } from '@store/social' +import type { Orga } from '@stores/social' type Props = { orga: Orga diff --git a/src/renderer/components/ui/requestRow.tsx b/src/renderer/components/ui/social/RequestRow.tsx similarity index 98% rename from src/renderer/components/ui/requestRow.tsx rename to src/renderer/components/ui/social/RequestRow.tsx index 65cd76e..403ba71 100644 --- a/src/renderer/components/ui/requestRow.tsx +++ b/src/renderer/components/ui/social/RequestRow.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import type { FriendRequest } from '@store/social' +import type { FriendRequest } from '@stores/social' type Props = { req: FriendRequest diff --git a/src/renderer/components/ui/socialSidebar.tsx b/src/renderer/components/ui/social/SocialSidebar.tsx similarity index 100% rename from src/renderer/components/ui/socialSidebar.tsx rename to src/renderer/components/ui/social/SocialSidebar.tsx diff --git a/src/renderer/src/content/lore/origins.md b/src/renderer/content/lore/origins.md similarity index 100% rename from src/renderer/src/content/lore/origins.md rename to src/renderer/content/lore/origins.md diff --git a/src/renderer/env.d.ts b/src/renderer/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/renderer/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/renderer/src/hooks/useFitWindowToContent.ts b/src/renderer/hooks/useFitWindowToContent.ts similarity index 100% rename from src/renderer/src/hooks/useFitWindowToContent.ts rename to src/renderer/hooks/useFitWindowToContent.ts diff --git a/src/renderer/src/i18n.ts b/src/renderer/i18n/index.ts similarity index 100% rename from src/renderer/src/i18n.ts rename to src/renderer/i18n/index.ts diff --git a/src/renderer/index.html b/src/renderer/index.html index 1a36909..1d065f0 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -12,6 +12,6 @@

- + diff --git a/src/renderer/src/lib/formatReleaseDate.ts b/src/renderer/lib/formatReleaseDate.ts similarity index 100% rename from src/renderer/src/lib/formatReleaseDate.ts rename to src/renderer/lib/formatReleaseDate.ts diff --git a/src/renderer/src/lib/isGameUpdateAvailable.ts b/src/renderer/lib/isGameUpdateAvailable.ts similarity index 100% rename from src/renderer/src/lib/isGameUpdateAvailable.ts rename to src/renderer/lib/isGameUpdateAvailable.ts diff --git a/src/renderer/src/main.tsx b/src/renderer/main.tsx similarity index 90% rename from src/renderer/src/main.tsx rename to src/renderer/main.tsx index 6afdb86..d94d025 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/main.tsx @@ -3,7 +3,7 @@ import './i18n' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' -import App from './app' +import App from './App' createRoot(document.getElementById('root')!).render( diff --git a/src/renderer/src/assets/electron.svg b/src/renderer/src/assets/electron.svg deleted file mode 100644 index 45ef09c..0000000 --- a/src/renderer/src/assets/electron.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/renderer/src/assets/wavy-lines.svg b/src/renderer/src/assets/wavy-lines.svg deleted file mode 100644 index d08c611..0000000 --- a/src/renderer/src/assets/wavy-lines.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/renderer/src/components/Versions.tsx b/src/renderer/src/components/Versions.tsx deleted file mode 100644 index 37a9ff0..0000000 --- a/src/renderer/src/components/Versions.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useState } from 'react' - -function Versions(): React.JSX.Element { - const [versions] = useState(window.electron.process.versions) - - return ( -
    -
  • Electron v{versions.electron}
  • -
  • Chromium v{versions.chrome}
  • -
  • Node v{versions.node}
  • -
- ) -} - -export default Versions diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts deleted file mode 100644 index 77cda03..0000000 --- a/src/renderer/src/env.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// - -interface ImportMetaEnv { - /** Page de statut (prod). Vide tant que non défini → bouton désactivé. */ - readonly VITE_STATUS_PAGE_UNIVERSE: string - /** Page de statut (test / preprod). */ - readonly VITE_STATUS_PAGE_TESTING: string - /** Intervalle de rafraîchissement statut serveur + joueurs (minutes). Défaut 5. */ - readonly VITE_SERVER_STATUS_POLL_MINUTES: string - /** Lien Discord où récupérer la nouvelle version du launcher (bandeau mise à jour). */ - readonly VITE_LAUNCHER_RELEASE_DISCORD_URL: string - /** Liens barre de navigation (vide → bouton désactivé). */ - readonly VITE_NAV_WEBSITE_URL: string - readonly VITE_NAV_DISCORD_URL: string - readonly VITE_NAV_WIKI_URL: string - readonly VITE_NAV_DONATE_URL: string - /** ID Cachet composant statut (prod). Défaut 3 si absent ou invalide. */ - readonly VITE_STATUS_COMPONENT_ID_UNIVERSE: string - /** ID Cachet composant statut (testing). Défaut 3 si absent ou invalide. */ - readonly VITE_STATUS_COMPONENT_ID_TESTING: string - /** ID Cachet métrique joueurs (prod). Défaut 2 si absent ou invalide. */ - readonly VITE_STATUS_METRIC_ID_UNIVERSE: string - /** ID Cachet métrique joueurs (testing). Défaut 2 si absent ou invalide. */ - readonly VITE_STATUS_METRIC_ID_TESTING: string -} - -interface ImportMeta { - readonly env: ImportMetaEnv -} \ No newline at end of file diff --git a/src/renderer/store/account.ts b/src/renderer/stores/account.ts similarity index 100% rename from src/renderer/store/account.ts rename to src/renderer/stores/account.ts diff --git a/src/renderer/store/availability.ts b/src/renderer/stores/availability.ts similarity index 100% rename from src/renderer/store/availability.ts rename to src/renderer/stores/availability.ts diff --git a/src/renderer/store/env.ts b/src/renderer/stores/env.ts similarity index 72% rename from src/renderer/store/env.ts rename to src/renderer/stores/env.ts index f8b2c3d..ccaae22 100644 --- a/src/renderer/store/env.ts +++ b/src/renderer/stores/env.ts @@ -1,6 +1,7 @@ import { create } from 'zustand' +import type { Env } from '@shared/types/env' -export type Env = 'universe' | 'universe-testing' +export type { Env } from '@shared/types/env' interface EnvState { activeEnv: Env diff --git a/src/renderer/store/files.ts b/src/renderer/stores/files.ts similarity index 100% rename from src/renderer/store/files.ts rename to src/renderer/stores/files.ts diff --git a/src/renderer/store/game.ts b/src/renderer/stores/game.ts similarity index 97% rename from src/renderer/store/game.ts rename to src/renderer/stores/game.ts index d4a34b8..f61523b 100644 --- a/src/renderer/store/game.ts +++ b/src/renderer/stores/game.ts @@ -1,7 +1,7 @@ import { create } from 'zustand' import { useEnvStore, type Env } from './env' import { useFilesStore } from './files' -import type { ServerStatusValue } from '../../main/services/gameStatus' +import type { ServerStatusValue } from '@shared/types/game' // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/src/renderer/store/lore.ts b/src/renderer/stores/lore.ts similarity index 65% rename from src/renderer/store/lore.ts rename to src/renderer/stores/lore.ts index a127a9f..351be59 100644 --- a/src/renderer/store/lore.ts +++ b/src/renderer/stores/lore.ts @@ -14,11 +14,7 @@ type LoreState = { } export const useLoreStore = create((set, get) => ({ - articles: [ - { id: 'origins', title: 'Origines', file: 'origins.md' }, - { id: 'factions', title: 'Factions', file: 'factions.md' }, - { id: 'universe', title: 'Univers', file: 'universe.md' } - ], + articles: [{ id: 'origins', title: 'Origines', file: 'origins.md' }], current: undefined, diff --git a/src/renderer/store/navigation.ts b/src/renderer/stores/navigation.ts similarity index 100% rename from src/renderer/store/navigation.ts rename to src/renderer/stores/navigation.ts diff --git a/src/renderer/store/social.ts b/src/renderer/stores/social.ts similarity index 100% rename from src/renderer/store/social.ts rename to src/renderer/stores/social.ts diff --git a/src/renderer/store/version.ts b/src/renderer/stores/version.ts similarity index 86% rename from src/renderer/store/version.ts rename to src/renderer/stores/version.ts index 112fb2f..3b38058 100644 --- a/src/renderer/store/version.ts +++ b/src/renderer/stores/version.ts @@ -1,12 +1,8 @@ import { create } from 'zustand' import type { Env } from './env' +import type { GameVersionInfo } from '@shared/types/version' -// ─── Types ──────────────────────────────────────────────────────────────────── - -export type GameVersionInfo = { - version: string | null - releaseDate: string | null -} +export type { GameVersionInfo } from '@shared/types/version' type VersionState = { // Launcher diff --git a/src/renderer/views/lore.tsx b/src/renderer/views/Lore.tsx similarity index 59% rename from src/renderer/views/lore.tsx rename to src/renderer/views/Lore.tsx index 59696e1..b899713 100644 --- a/src/renderer/views/lore.tsx +++ b/src/renderer/views/Lore.tsx @@ -1,5 +1,5 @@ -import LoreSidebar from '@components/ui/loreSidebar' -import LoreArticle from '@components/ui/loreArticle' +import LoreSidebar from '@components/ui/lore/LoreSidebar' +import LoreArticle from '@components/ui/lore/LoreArticle' export default function LorePage() { return ( diff --git a/src/renderer/views/social.tsx b/src/renderer/views/Social.tsx similarity index 95% rename from src/renderer/views/social.tsx rename to src/renderer/views/Social.tsx index 18eaba2..81b16da 100644 --- a/src/renderer/views/social.tsx +++ b/src/renderer/views/Social.tsx @@ -1,12 +1,12 @@ import { useEffect, useState } from 'react' -import { useSocialStore } from '@store/social' -import { useEnvStore } from '@store/env' -import SocialSidebar from '@components/ui/socialSidebar' -import Button from '@components/ui/button' -import InputField from '@components/ui/inputField' -import { FriendRow } from '@components/ui/friendRow' -import { OrgaRow } from '@components/ui/orgaRow' -import { RequestRow } from '@components/ui/requestRow' +import { useSocialStore } from '@stores/social' +import { useEnvStore } from '@stores/env' +import SocialSidebar from '@components/ui/social/SocialSidebar' +import Button from '@components/ui/primitives/Button' +import InputField from '@components/ui/primitives/InputField' +import { FriendRow } from '@components/ui/social/FriendRow' +import { OrgaRow } from '@components/ui/social/OrgaRow' +import { RequestRow } from '@components/ui/social/RequestRow' // ─── Formulaire : ajouter un ami ────────────────────────────────────────────── diff --git a/src/renderer/views/UniverseView.tsx b/src/renderer/views/UniverseView.tsx new file mode 100644 index 0000000..cfec0b5 --- /dev/null +++ b/src/renderer/views/UniverseView.tsx @@ -0,0 +1,58 @@ +import { useNavigationStore } from '@stores/navigation' +import { useVersionStore } from '@stores/version' +import { useTranslation } from 'react-i18next' +import AccountPanel from '@components/panel/AccountPanel' +import GamePanel from '@components/panel/GamePanel' +import SocialPanel from '@components/panel/SocialPanel' +import FilesPanel from '@components/panel/FilesPanel' +import UpdateAlert from '@components/ui/feedback/UpdateAlert' + +type UniverseViewProps = { + showTestingBanner?: boolean +} + +export default function UniverseView({ + showTestingBanner = false +}: UniverseViewProps): React.JSX.Element { + useNavigationStore() + const { t } = useTranslation() + const { + launcherUpdateAvailable, + currentLauncherVersion, + latestLauncherVersion, + latestLauncherReleaseDate + } = useVersionStore() + + return ( +
+ {showTestingBanner && ( +
+ + + {t('universeTesting.banner')} + +
+ )} + + {launcherUpdateAvailable && latestLauncherVersion && ( +
+ +
+ )} + +
+ + + + +
+
+ ) +} diff --git a/src/renderer/views/universe.tsx b/src/renderer/views/universe.tsx deleted file mode 100644 index a8edfa5..0000000 --- a/src/renderer/views/universe.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useNavigationStore } from '@store/navigation' -import { useVersionStore } from '@store/version' -import AccountPanel from '@components/panel/accountPanel' -import GamePanel from '@components/panel/gamePanel' -import SocialPanel from '@components/panel/socialPanel' -import FilesPanel from '@components/panel/filesPanel' -import UpdateAlert from '@components/ui/updateAlert' - -export default function Universe(): React.JSX.Element { - useNavigationStore() - const { - launcherUpdateAvailable, - currentLauncherVersion, - latestLauncherVersion, - latestLauncherReleaseDate - } = useVersionStore() - - return ( -
- - {/* Alerte launcher */} - {launcherUpdateAvailable && latestLauncherVersion && ( -
- -
- )} - -
- - - - -
-
- ) -} \ No newline at end of file diff --git a/src/renderer/views/universeTesting.tsx b/src/renderer/views/universeTesting.tsx deleted file mode 100644 index 5d9462a..0000000 --- a/src/renderer/views/universeTesting.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useNavigationStore } from '@store/navigation' -import { useVersionStore } from '@store/version' -import { useTranslation } from 'react-i18next' -import AccountPanel from '@components/panel/accountPanel' -import GamePanel from '@components/panel/gamePanel' -import SocialPanel from '@components/panel/socialPanel' -import FilesPanel from '@components/panel/filesPanel' -import UpdateAlert from '@components/ui/updateAlert' - -export default function UniverseTesting(): React.JSX.Element { - useNavigationStore() - const { t } = useTranslation() - const { - launcherUpdateAvailable, - currentLauncherVersion, - latestLauncherVersion, - latestLauncherReleaseDate - } = useVersionStore() - - return ( -
- -
- - - {t('universeTesting.banner')} - -
- - {launcherUpdateAvailable && latestLauncherVersion && ( -
- -
- )} - -
- - - - -
-
- ) -} diff --git a/src/main/vite-env.d.ts b/src/shared/env.d.ts similarity index 52% rename from src/main/vite-env.d.ts rename to src/shared/env.d.ts index 6b04ab3..8be8ca8 100644 --- a/src/main/vite-env.d.ts +++ b/src/shared/env.d.ts @@ -1,34 +1,32 @@ -/** Complète ImportMetaEnv pour les URLs API embarquées au build (voir electron-vite / Vite). */ +/** Variables d'environnement Vite (main + renderer). */ interface ImportMetaEnv { readonly VITE_API_BASE_UNIVERSE: string readonly VITE_API_BASE_TESTING: string - /** Base URL des archives jeu (ZIP). Si vide, identique à VITE_API_BASE_*. */ readonly VITE_GAME_DOWNLOAD_BASE_UNIVERSE: string readonly VITE_GAME_DOWNLOAD_BASE_TESTING: string - /** ZIP env prod (universe) — URLs complètes (prioritaires sur /game/latest-*.zip). */ readonly VITE_GAME_ZIP_LINUX: string readonly VITE_GAME_ZIP_WINDOWS: string readonly VITE_GAME_ZIP_DARWIN: string - /** ZIP env test — URLs complètes (prioritaires sur /game/latest-*.zip). */ readonly VITE_GAME_ZIP_TESTING_WINDOWS: string readonly VITE_GAME_ZIP_TESTING_LINUX: string readonly VITE_GAME_ZIP_TESTING_DARWIN: string - /** URL du dépôt GitHub du launcher (page releases), ex. https://github.com/org/launcher */ readonly VITE_LAUNCHER_GITHUB_REPO_URL: string - /** Largeur fenêtre (px), défaut 1200 */ + readonly VITE_LAUNCHER_RELEASE_DISCORD_URL: string readonly VITE_WINDOW_WIDTH: string - /** Hauteur fenêtre (px), défaut 800 */ readonly VITE_WINDOW_HEIGHT: string - /** `"true"` pour ouvrir les DevTools au démarrage */ readonly VITE_ENABLE_DEVTOOLS: string - /** `"true"` pour niveau de log fichier debug (electron-log) */ readonly VITE_ELECTRON_ENABLE_LOGGING: string - /** ID Cachet `/api/components/:id` (universe). Entier > 0 ; défaut logique 3 dans env.ts si absent. */ readonly VITE_STATUS_COMPONENT_ID_UNIVERSE: string readonly VITE_STATUS_COMPONENT_ID_TESTING: string - /** ID Cachet `/api/metrics/:id/points` (joueurs). Défaut 2 dans env.ts si absent. */ readonly VITE_STATUS_METRIC_ID_UNIVERSE: string readonly VITE_STATUS_METRIC_ID_TESTING: string + readonly VITE_STATUS_PAGE_UNIVERSE: string + readonly VITE_STATUS_PAGE_TESTING: string + readonly VITE_SERVER_STATUS_POLL_MINUTES: string + readonly VITE_NAV_WEBSITE_URL: string + readonly VITE_NAV_DISCORD_URL: string + readonly VITE_NAV_WIKI_URL: string + readonly VITE_NAV_DONATE_URL: string } interface ImportMeta { diff --git a/src/shared/types/auth.ts b/src/shared/types/auth.ts new file mode 100644 index 0000000..aa8f1a7 --- /dev/null +++ b/src/shared/types/auth.ts @@ -0,0 +1,5 @@ +export interface UserInfo { + sub: string + username: string + email: string +} diff --git a/src/shared/types/env.ts b/src/shared/types/env.ts new file mode 100644 index 0000000..bdab956 --- /dev/null +++ b/src/shared/types/env.ts @@ -0,0 +1,2 @@ +/** Environnement cible (prod ou test). */ +export type Env = 'universe' | 'universe-testing' diff --git a/src/shared/types/game.ts b/src/shared/types/game.ts new file mode 100644 index 0000000..a787403 --- /dev/null +++ b/src/shared/types/game.ts @@ -0,0 +1,14 @@ +export type ServerStatusValue = + | 'online' + | 'degraded' + | 'offline' + | 'maintenance' + | 'unknown' + | 'unavailable' + +export interface GameStatusResult { + status: ServerStatusValue + statusLabel: string + players: number + available: boolean +} diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts new file mode 100644 index 0000000..a8fc4b0 --- /dev/null +++ b/src/shared/types/index.ts @@ -0,0 +1,5 @@ +export type { Env } from './env' +export type { ServerStatusValue, GameStatusResult } from './game' +export type { UserInfo } from './auth' +export type { GameVersionInfo, VersionCheckResult } from './version' +export type { InstallResult } from './install' diff --git a/src/shared/types/install.ts b/src/shared/types/install.ts new file mode 100644 index 0000000..7ed14e2 --- /dev/null +++ b/src/shared/types/install.ts @@ -0,0 +1,4 @@ +export interface InstallResult { + version: string + releaseDate: string +} diff --git a/src/shared/types/version.ts b/src/shared/types/version.ts new file mode 100644 index 0000000..f9c923a --- /dev/null +++ b/src/shared/types/version.ts @@ -0,0 +1,14 @@ +import type { Env } from './env' + +export interface GameVersionInfo { + version: string | null + releaseDate: string | null +} + +export interface VersionCheckResult { + currentLauncherVersion: string + latestLauncherVersion: string | null + latestLauncherReleaseDate: string | null + launcherUpdateAvailable: boolean + latestGameVersions: Record +} diff --git a/tsconfig.json b/tsconfig.json index eaa0418..a99c5ba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,10 +3,6 @@ "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }], "compilerOptions": { "strict": true, - "noImplicitAny": true, - "paths": { - "@renderer/*": ["./src/renderer/*"], - "@main/*": ["./src/main/*"] - } + "noImplicitAny": true } } diff --git a/tsconfig.node.json b/tsconfig.node.json index 0563ccb..d52c76d 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -1,8 +1,17 @@ { - "extends": "./node_modules/@electron-toolkit/tsconfig/tsconfig.node.json", - "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"], + "extends": "@electron-toolkit/tsconfig/tsconfig.node.json", + "include": [ + "electron.vite.config.*", + "src/main/**/*", + "src/preload/**/*", + "src/shared/**/*" + ], "compilerOptions": { "composite": true, - "types": ["electron-vite/node"] + "types": ["electron-vite/node"], + "baseUrl": ".", + "paths": { + "@shared/*": ["src/shared/*"] + } } } diff --git a/tsconfig.web.json b/tsconfig.web.json index 7c4348e..2bdaa4d 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -1,20 +1,24 @@ { "extends": "@electron-toolkit/tsconfig/tsconfig.web.json", "include": [ - "src/renderer/src/env.d.ts", - "src/renderer/src/**/*", - "src/renderer/src/**/*.tsx", - "src/preload/*.d.ts" + "src/renderer/**/*", + "src/shared/env.d.ts", + "src/preload/index.d.ts" ], "compilerOptions": { "composite": true, "jsx": "react-jsx", "baseUrl": ".", "paths": { - "@renderer/*": ["src/renderer/src/*"], + "@shared/*": ["src/shared/*"], + "@hooks/*": ["src/renderer/hooks/*"], + "@lib/*": ["src/renderer/lib/*"], + "@assets/*": ["src/renderer/assets/*"], + "@content/*": ["src/renderer/content/*"], + "@stores/*": ["src/renderer/stores/*"], "@components/*": ["src/renderer/components/*"], "@views/*": ["src/renderer/views/*"], - "@store/*": ["src/renderer/store/*"], + "@i18n": ["src/renderer/i18n/index.ts"], "@i18n/*": ["src/renderer/i18n/*"] } } From 6caeacc0076f6eb70bfad9a6f367e588f9a2ed85 Mon Sep 17 00:00:00 2001 From: maestrix Date: Fri, 15 May 2026 23:33:14 +0200 Subject: [PATCH 2/5] feat: refactong complete + mardown for project + setting prettier + eslint --- .env | 2 +- .env.example | 43 + .vscode/extensions.json | 2 +- .vscode/settings.json | 9 + README.md | 59 +- docs/README.md | 10 + docs/en/CHANGELOG.md | 33 + docs/en/CODE_OF_CONDUCT.md | 32 + docs/en/CONTRIBUTING.md | 58 + docs/en/ONBOARDING.md | 166 + docs/en/SECURITY.md | 36 + docs/fr/CHANGELOG.md | 33 + docs/fr/CODE_OF_CONDUCT.md | 32 + docs/fr/CONTRIBUTING.md | 58 + docs/fr/ONBOARDING.md | 166 + docs/fr/SECURITY.md | 36 + eslint.config.mjs | 3 +- package-lock.json | 12257 ---------------- package.json | 11 +- src/main/config/api.ts | 60 +- src/main/config/auth.ts | 19 + src/main/config/constants.ts | 48 + src/main/icon.ts | 16 +- src/main/index.ts | 99 +- src/main/l10n/dialogs.ts | 27 + src/main/protocol/linuxDesktop.ts | 49 + src/main/services/auth.ts | 356 - src/main/services/auth/flow.ts | 168 + src/main/services/auth/handlers.ts | 30 + src/main/services/auth/index.ts | 3 + src/main/services/auth/jwt.ts | 88 + src/main/services/auth/keycloakClient.ts | 58 + src/main/services/auth/pkce.ts | 13 + src/main/services/auth/tokenStorage.ts | 43 + src/main/services/auth/types.ts | 17 + src/main/services/download/extract.ts | 26 + src/main/services/download/index.ts | 2 + src/main/services/download/install.ts | 43 + src/main/services/download/manifest.ts | 67 + src/main/services/download/progress.ts | 12 + src/main/services/download/zipFetch.ts | 115 + src/main/services/download/zipUrl.ts | 30 + src/main/services/downloader.ts | 269 - src/main/services/game.ts | 245 - src/main/services/game/handlers.ts | 78 + src/main/services/game/index.ts | 2 + src/main/services/game/launch.ts | 39 + src/main/services/game/paths.ts | 61 + src/main/services/game/processWatch.ts | 71 + src/main/services/gameStatus.ts | 41 +- src/main/services/godotUserdataCache.ts | 63 +- src/main/services/version.ts | 43 +- src/main/services/window.ts | 13 +- src/preload/index.d.ts | 13 +- src/preload/index.ts | 83 +- src/renderer/App.tsx | 12 +- src/renderer/assets/main.css | 123 +- src/renderer/components/layout/Navbar.tsx | 276 +- src/renderer/components/layout/Shell.tsx | 23 +- .../components/panel/AccountPanel.tsx | 21 +- src/renderer/components/panel/FilesPanel.tsx | 45 +- src/renderer/components/panel/GamePanel.tsx | 71 +- src/renderer/components/panel/SocialPanel.tsx | 18 +- .../components/ui/feedback/PanelMessage.tsx | 5 +- .../components/ui/feedback/ServerStatus.tsx | 19 +- .../components/ui/feedback/UpdateAlert.tsx | 69 +- .../components/ui/lore/LoreArticle.tsx | 56 +- .../components/ui/lore/LoreSidebar.tsx | 19 +- .../components/ui/overlays/ChangelogModal.tsx | 35 +- .../components/ui/primitives/Button.tsx | 2 +- .../components/ui/primitives/InputField.tsx | 3 +- .../ui/primitives/icons/DiscordIcon.tsx | 6 +- .../components/ui/social/FriendItem.tsx | 31 +- .../components/ui/social/FriendRow.tsx | 23 +- src/renderer/components/ui/social/OrgaRow.tsx | 61 +- .../components/ui/social/RequestRow.tsx | 49 +- .../components/ui/social/SocialSidebar.tsx | 27 +- src/renderer/content/lore/origins.md | 2 +- src/renderer/hooks/useFitWindowToContent.ts | 15 +- src/renderer/i18n/en.json | 274 +- src/renderer/i18n/fr.json | 272 +- src/renderer/i18n/index.ts | 54 +- src/renderer/lib/env.ts | 51 + src/renderer/lib/formatInstallProgress.ts | 44 + src/renderer/lib/formatReleaseDate.ts | 3 +- src/renderer/lib/isGameUpdateAvailable.ts | 9 +- src/renderer/lib/socialErrors.ts | 29 + src/renderer/stores/account.ts | 54 +- src/renderer/stores/availability.ts | 18 +- src/renderer/stores/env.ts | 6 +- src/renderer/stores/files.ts | 93 +- src/renderer/stores/game.ts | 20 +- src/renderer/stores/lore.ts | 12 +- src/renderer/stores/navigation.ts | 7 +- src/renderer/stores/social.ts | 114 +- src/renderer/stores/version.ts | 46 +- src/renderer/views/Lore.tsx | 8 +- src/renderer/views/Social.tsx | 138 +- src/renderer/views/UniverseView.tsx | 11 +- src/shared/env.d.ts | 20 +- src/shared/types/auth.ts | 1 + src/shared/types/env.ts | 2 +- src/shared/types/game.ts | 2 + src/shared/types/install.ts | 1 + src/shared/types/installProgress.ts | 11 + src/shared/types/version.ts | 2 + tailwind.config.ts | 30 +- 107 files changed, 3434 insertions(+), 14365 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/en/CHANGELOG.md create mode 100644 docs/en/CODE_OF_CONDUCT.md create mode 100644 docs/en/CONTRIBUTING.md create mode 100644 docs/en/ONBOARDING.md create mode 100644 docs/en/SECURITY.md create mode 100644 docs/fr/CHANGELOG.md create mode 100644 docs/fr/CODE_OF_CONDUCT.md create mode 100644 docs/fr/CONTRIBUTING.md create mode 100644 docs/fr/ONBOARDING.md create mode 100644 docs/fr/SECURITY.md delete mode 100644 package-lock.json create mode 100644 src/main/config/auth.ts create mode 100644 src/main/config/constants.ts create mode 100644 src/main/l10n/dialogs.ts create mode 100644 src/main/protocol/linuxDesktop.ts delete mode 100644 src/main/services/auth.ts create mode 100644 src/main/services/auth/flow.ts create mode 100644 src/main/services/auth/handlers.ts create mode 100644 src/main/services/auth/index.ts create mode 100644 src/main/services/auth/jwt.ts create mode 100644 src/main/services/auth/keycloakClient.ts create mode 100644 src/main/services/auth/pkce.ts create mode 100644 src/main/services/auth/tokenStorage.ts create mode 100644 src/main/services/auth/types.ts create mode 100644 src/main/services/download/extract.ts create mode 100644 src/main/services/download/index.ts create mode 100644 src/main/services/download/install.ts create mode 100644 src/main/services/download/manifest.ts create mode 100644 src/main/services/download/progress.ts create mode 100644 src/main/services/download/zipFetch.ts create mode 100644 src/main/services/download/zipUrl.ts delete mode 100644 src/main/services/downloader.ts delete mode 100644 src/main/services/game.ts create mode 100644 src/main/services/game/handlers.ts create mode 100644 src/main/services/game/index.ts create mode 100644 src/main/services/game/launch.ts create mode 100644 src/main/services/game/paths.ts create mode 100644 src/main/services/game/processWatch.ts create mode 100644 src/renderer/lib/env.ts create mode 100644 src/renderer/lib/formatInstallProgress.ts create mode 100644 src/renderer/lib/socialErrors.ts create mode 100644 src/shared/types/installProgress.ts diff --git a/.env b/.env index 16b84a1..8270548 100644 --- a/.env +++ b/.env @@ -63,7 +63,7 @@ DEBUG_ELECTRON=true # OPTIONAL SETTINGS (main — VITE_* figées au build) # =========================================== VITE_WINDOW_WIDTH=1200 -VITE_WINDOW_HEIGHT=800 +VITE_WINDOW_HEIGHT=750 VITE_ENABLE_DEVTOOLS=false VITE_ELECTRON_ENABLE_LOGGING=true diff --git a/.env.example b/.env.example index 309c784..2616db8 100644 --- a/.env.example +++ b/.env.example @@ -74,3 +74,46 @@ VITE_ENABLE_DEVTOOLS=true # Logs fichier au niveau debug VITE_ELECTRON_ENABLE_LOGGING=true +# =========================================== +# HTTP / DOWNLOAD / GAME (main process) +# =========================================== +# Timeouts in milliseconds +# VITE_HTTP_TIMEOUT_API_MS=8000 +# VITE_HTTP_TIMEOUT_VERSION_PING_MS=5000 +# VITE_HTTP_TIMEOUT_GITHUB_MS=10000 +# VITE_DOWNLOAD_TIMEOUT_MS=600000 +# VITE_GAME_PROCESS_POLL_MS=2000 + +# Optional download host (defaults to API base when empty) +# VITE_GAME_DOWNLOAD_BASE_UNIVERSE= +# VITE_GAME_DOWNLOAD_BASE_TESTING= + +# Cachet status API ids (defaults: component 3, metric 2) +# VITE_STATUS_COMPONENT_ID_UNIVERSE=3 +# VITE_STATUS_COMPONENT_ID_TESTING=3 +# VITE_STATUS_METRIC_ID_UNIVERSE=2 +# VITE_STATUS_METRIC_ID_TESTING=2 + +# Game install layout +# VITE_GAME_INSTALL_SUBDIR=DyingStar +# VITE_GAME_EXE_WINDOWS=DyingStar.exe +# VITE_GAME_EXE_LINUX=DyingStar.x86_64 +# VITE_GAME_EXE_DARWIN=DyingStar.app + +# OAuth / custom protocol +# VITE_APP_PROTOCOL=dyingstar +# VITE_AUTH_CLIENT_ID=dyingstar-launcher +# VITE_AUTH_REALM=dyingstar +# VITE_AUTH_REDIRECT_URI=dyingstar://auth/callback +# VITE_DOWNLOAD_USER_AGENT=DyingStar-Launcher/1.0 + +# Window max size when auto-fitting content +# VITE_WINDOW_MAX_WIDTH=1920 +# VITE_WINDOW_MAX_HEIGHT=1200 + +# =========================================== +# UI TIMING (renderer) +# =========================================== +# VITE_UI_TOAST_DURATION_MS=3800 +# VITE_UI_SOCIAL_PANEL_CLOSE_MS=1200 + diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 940260d..1d7ac85 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["dbaeumer.vscode-eslint"] + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c05394..c0ffdc1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,16 @@ { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, diff --git a/README.md b/README.md index c1fd0dc..c75ed5d 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ Launcher desktop open source pour le jeu **Dying Star**, basé sur [Electron](ht ## Stack -| Couche | Technologie | -|--------|-------------| -| Shell desktop | Electron 38 | -| Build | electron-vite, Vite 7 | -| UI | React 19, Tailwind CSS v4 | -| État | Zustand | -| i18n | i18next / react-i18next | -| Packaging | electron-builder | +| Couche | Technologie | +| ------------- | ------------------------- | +| Shell desktop | Electron 38 | +| Build | electron-vite, Vite 7 | +| UI | React 19, Tailwind CSS v4 | +| État | Zustand | +| i18n | i18next / react-i18next | +| Packaging | electron-builder | ## Structure du projet @@ -21,6 +21,7 @@ src/ ├── preload/ # Pont sécurisé contextBridge → window.api ├── renderer/ # Application React (composants, vues, stores, i18n) └── shared/ # Types TypeScript partagés (main, preload, renderer) +docs/ # Documentation contributeur (en/ et fr/) resources/ # Icônes et entitlements macOS (versionnés) ``` @@ -29,31 +30,33 @@ Alias TypeScript / Vite : `@shared`, `@components`, `@views`, `@stores`, `@hooks ## Prérequis - Node.js 20+ -- npm ou pnpm +- [pnpm](https://pnpm.io/) (gestionnaire de paquets du dépôt) ## Installation ```bash git clone https://github.com/DyingStar-game/launcher.git cd launcher -npm install +git checkout develop +pnpm install cp .env.example .env # Éditer .env selon l’environnement (API, ZIP de test, etc.) ``` ## Scripts -| Commande | Description | -|----------|-------------| -| `npm run dev` | Développement avec rechargement à chaud | -| `npm run build` | Vérification TypeScript + build de production (`out/`) | -| `npm run start` | Prévisualiser le build packagé | -| `npm run typecheck` | Contrôle TypeScript (main + renderer) | -| `npm run lint` | ESLint | -| `npm run format` | Prettier | -| `npm run build:win` | Installateur Windows (NSIS) | -| `npm run build:mac` | Application macOS | -| `npm run build:linux` | AppImage / deb / rpm | +| Commande | Description | +| ---------------------- | ------------------------------------------------------ | +| `pnpm run dev` | Développement avec rechargement à chaud | +| `pnpm run build` | Vérification TypeScript + build de production (`out/`) | +| `pnpm run start` | Prévisualiser le build packagé | +| `pnpm run typecheck` | Contrôle TypeScript (main + renderer) | +| `pnpm run lint` | ESLint | +| `pnpm run lint:fix` | ESLint avec corrections automatiques | +| `pnpm run format` | Prettier | +| `pnpm run build:win` | Installateur Windows (NSIS) | +| `pnpm run build:mac` | Application macOS | +| `pnpm run build:linux` | AppImage / deb / rpm | Les artefacts de distribution sont générés dans `release/`. @@ -63,10 +66,16 @@ Copier `.env.example` vers `.env`. Les variables exposées au code utilisent le ## Contribution -1. Créer une branche depuis `develop` -2. `npm run typecheck && npm run lint` avant toute PR -3. Respecter la structure `src/main`, `src/preload`, `src/renderer`, `src/shared` +**Branche de développement par défaut : `develop`** (ne pas partir de `main` pour le dev quotidien). + +Documentation bilingue : + +- Index : [docs/README.md](docs/README.md) +- Onboarding : [English](docs/en/ONBOARDING.md) · [Français](docs/fr/ONBOARDING.md) +- Contributing : [English](docs/en/CONTRIBUTING.md) · [Français](docs/fr/CONTRIBUTING.md) + +Avant une PR : `pnpm run typecheck && pnpm run lint` ## Licence -Voir le dépôt GitHub du projet pour les conditions de licence. +[MIT License](LICENSE) — Copyright (c) Dying Star diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ff533f9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +# Project documentation + +Bilingual documentation for contributors and maintainers. + +| Language | Onboarding | Contributing | Code of conduct | Security | Changelog | +| -------- | ------------------------------------ | ---------------------------------------- | ---------------------------------------------- | -------------------------------- | ---------------------------------- | +| English | [en/ONBOARDING.md](en/ONBOARDING.md) | [en/CONTRIBUTING.md](en/CONTRIBUTING.md) | [en/CODE_OF_CONDUCT.md](en/CODE_OF_CONDUCT.md) | [en/SECURITY.md](en/SECURITY.md) | [en/CHANGELOG.md](en/CHANGELOG.md) | +| Français | [fr/ONBOARDING.md](fr/ONBOARDING.md) | [fr/CONTRIBUTING.md](fr/CONTRIBUTING.md) | [fr/CODE_OF_CONDUCT.md](fr/CODE_OF_CONDUCT.md) | [fr/SECURITY.md](fr/SECURITY.md) | [fr/CHANGELOG.md](fr/CHANGELOG.md) | + +**Default development branch:** `develop` (create feature branches from `develop`, not `main`). diff --git a/docs/en/CHANGELOG.md b/docs/en/CHANGELOG.md new file mode 100644 index 0000000..7209c27 --- /dev/null +++ b/docs/en/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to this project are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Bilingual contributor docs under `docs/en/` and `docs/fr/` (onboarding, contributing, security, code of conduct) +- Structured install progress labels with full UI i18n (`installProgress.*`) +- Browser language detection and persisted UI language (`ds-language`) + +### Changed + +- Package manager standardized on **pnpm** (removed `package-lock.json`) +- Native install folder dialog localized (fr/en) via main-process l10n +- Social `addFriend` mock validates duplicate names and throws typed errors + +### Removed + +- Unused `game:get-state` IPC stub + +## [0.1.0] - 2025-01-01 + +### Added + +- Initial open-source Electron launcher: auth, game install/update, server status, lore, social UI (mock) + +[Unreleased]: https://github.com/DyingStar-game/launcher/compare/v0.1.0...develop +[0.1.0]: https://github.com/DyingStar-game/launcher/releases/tag/v0.1.0 diff --git a/docs/en/CODE_OF_CONDUCT.md b/docs/en/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..10356dd --- /dev/null +++ b/docs/en/CODE_OF_CONDUCT.md @@ -0,0 +1,32 @@ +# Contributor Code of Conduct + +## Our pledge + +We pledge to make participation in the Dying Star Launcher project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our standards + +Examples of behavior that contributes to a positive environment: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy toward other community members + +Examples of unacceptable behavior: + +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement + +Project maintainers may remove, edit, or reject comments, commits, code, issues, and other contributions that are not aligned with this Code of Conduct, and may ban contributors temporarily or permanently. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to **contact@dyingstar-game.com**. All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1. diff --git a/docs/en/CONTRIBUTING.md b/docs/en/CONTRIBUTING.md new file mode 100644 index 0000000..962438f --- /dev/null +++ b/docs/en/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing + +Thank you for contributing to Dying Star Launcher. + +## Quick links + +- [Onboarding](ONBOARDING.md) — architecture, setup, branches +- [Code of conduct](CODE_OF_CONDUCT.md) +- [Security policy](SECURITY.md) +- [Changelog](CHANGELOG.md) + +## Development workflow + +1. Fork the repository (if external contributor) and clone locally +2. Check out **`develop`** and create a branch: `feature/short-description` or `fix/short-description` +3. Install dependencies with **pnpm only**: `pnpm install` +4. Copy `.env.example` to `.env` and configure as needed +5. Make your changes +6. Run checks: + ```bash + pnpm run format + pnpm run typecheck + pnpm run lint + ``` +7. Open a Pull Request **into `develop`** + +## Code guidelines + +- **TypeScript** everywhere; avoid `any` unless justified +- **Comments and JSDoc** on public APIs and non-obvious logic — in **English** +- **User-facing strings** only via i18n (`src/renderer/i18n/*.json`), not hardcoded in components +- **IPC**: add handlers in `src/main`, expose in `src/preload/index.ts`, types in `src/preload/index.d.ts` and `@shared/types` when shared +- Keep PRs small and focused; one topic per PR when possible + +## Commit messages + +Use clear, imperative subjects, e.g.: + +- `feat(files): add install path validation` +- `fix(auth): handle cancelled OAuth flow` +- `docs: update onboarding for pnpm` + +## Pull request checklist + +- [ ] Based on latest `develop` +- [ ] `pnpm run typecheck` passes +- [ ] `pnpm run lint` passes (or only pre-existing warnings documented) +- [ ] User-visible changes reflected in `docs/en/CHANGELOG.md` and `docs/fr/CHANGELOG.md` +- [ ] No secrets or `.env` files committed +- [ ] Screenshots attached for UI changes (optional but appreciated) + +## Releases + +Maintainers merge `develop` → `main` and tag releases. Built artifacts are published via GitHub Releases (see root `README.md` build scripts). + +## License + +By contributing, you agree that your contributions are licensed under the [MIT License](../../LICENSE) of this project. diff --git a/docs/en/ONBOARDING.md b/docs/en/ONBOARDING.md new file mode 100644 index 0000000..6250d19 --- /dev/null +++ b/docs/en/ONBOARDING.md @@ -0,0 +1,166 @@ +# Contributor onboarding + +Welcome to the **Dying Star Launcher** repository. This guide helps you understand the project and get a working dev environment quickly. + +## What this project is + +A desktop **Electron** launcher for the game _Dying Star_. It handles: + +- Discord / Keycloak authentication (per environment) +- Game download, install, and updates (ZIP from configurable URLs) +- Server status and player count +- Launcher update checks (GitHub Releases) +- Lore articles (bundled markdown) and a **social UI** (currently mock data) + +## Repository layout + +``` +src/ +├── main/ # Electron main process (IPC, auth, download, game launch) +├── preload/ # contextBridge API exposed as window.api +├── renderer/ # React UI (views, components, Zustand stores, i18n) +└── shared/ # TypeScript types shared across processes +docs/ # Contributor docs (en/ and fr/) +resources/ # App icons and macOS entitlements +``` + +Path aliases (Vite/TS): `@shared`, `@components`, `@views`, `@stores`, `@hooks`, `@lib`, `@i18n`, `@content`, `@assets`. + +## Branches + +| Branch | Purpose | +| ---------------- | ----------------------------------------------------------- | +| **`develop`** | **Default branch for development.** Open PRs here. | +| `main` | Stable / release-aligned (maintainers merge from `develop`) | +| Feature branches | `feature/your-topic` from `develop` | + +```bash +git checkout develop +git pull origin develop +git checkout -b feature/my-change +``` + +## Prerequisites + +- **Node.js 20+** +- **pnpm** (only package manager used in this repo — see root `packageManager` in `package.json`) +- Git + +## First-time setup + +```bash +git clone https://github.com/DyingStar-game/launcher.git +cd launcher +git checkout develop +pnpm install +cp .env.example .env +# Edit .env — at minimum set API/ZIP URLs for universe-testing if you want installs +pnpm run dev +``` + +### Environment files + +- Copy `.env.example` → `.env` (never commit `.env`) +- Variables prefixed with `VITE_` are embedded at build time in the app binary +- For local UI-only work, testing URLs in `.env.example` may be enough; production URLs are often empty until configured + +### Useful scripts + +| Command | Description | +| -------------------- | ---------------------------- | +| `pnpm run dev` | Hot-reload development | +| `pnpm run typecheck` | TypeScript (main + renderer) | +| `pnpm run lint` | ESLint | +| `pnpm run format` | Prettier | +| `pnpm run build` | Production build to `out/` | + +## ESLint and Prettier + +The repo ships with **ESLint 9** (flat config) and **Prettier**. They are installed with `pnpm install` — no global install required. + +### Configuration files + +| File | Role | +| ------------------- | ------------------------------------------------------------------- | +| `eslint.config.mjs` | ESLint flat config (TypeScript, React, Hooks, Prettier integration) | +| `.prettierrc.yaml` | Prettier rules (single quotes, no semicolons, print width 100) | +| `.prettierignore` | Paths excluded from formatting (`out/`, lockfiles, etc.) | + +ESLint uses `@electron-toolkit/eslint-config-prettier` so formatting is handled by Prettier, not duplicate ESLint style rules. + +### Commands (repo root) + +```bash +# Lint check (required before a PR) +pnpm run lint + +# Auto-fix what ESLint can (including Prettier issues reported via eslint) +pnpm run lint:fix + +# Format the whole project with Prettier +pnpm run format +``` + +Recommended workflow before committing: + +```bash +pnpm run format +pnpm run typecheck +pnpm run lint +``` + +### VS Code / Cursor + +Recommended workspace files live in `.vscode/`: + +1. Install extensions (folder prompt or manually): + - **ESLint** (`dbaeumer.vscode-eslint`) + - **Prettier** (`esbenp.prettier-vscode`) +2. `.vscode/settings.json` sets Prettier as the default formatter and enables format on save for TypeScript/JavaScript/JSON. + +If format on save does not run, set **Default Formatter** to Prettier and enable **Editor: Format On Save**. + +### CI + +When GitHub Actions is added, run `pnpm run typecheck` and `pnpm run lint` on every PR to `develop`. + +## How the app is structured (mental model) + +1. **Renderer** (React + Zustand) calls **`window.api.*`** from the preload script. +2. **Preload** forwards calls via `ipcRenderer.invoke` to **main** handlers. +3. **Main** performs filesystem, network, OAuth, and game process work, then pushes events back (e.g. auth state, install progress). + +Install progress uses **structured keys** (`InstallProgressLabel` in `src/shared/types/installProgress.ts`) sent from main; the renderer translates them with i18next (`installProgress.*` keys). + +## Internationalization (i18n) + +- UI strings: `src/renderer/i18n/en.json` and `fr.json` +- Use `useTranslation()` and `t('key')` in components — no user-facing hardcoded strings in TSX +- Language: browser detection + `localStorage` key `ds-language`; navbar flags switch EN/FR +- Native folder picker (main): `src/main/l10n/dialogs.ts` (OS locale fr/en) + +## Features to know about + +### Social page (mock) + +`src/renderer/stores/social.ts` uses **in-memory mock data** until backend APIs exist. Friend/org actions simulate latency; errors use `SocialStoreError` codes mapped in `src/renderer/lib/socialErrors.ts`. + +### Two game environments + +- `universe` — production +- `universe-testing` — test server / data + +The navbar env switcher changes `useEnvStore().activeEnv`; most stores are keyed by `Env`. + +## Before opening a PR + +1. Branch from **`develop`** +2. Run `pnpm run typecheck && pnpm run lint` +3. Keep changes focused; match existing code style +4. Update `docs/en/CHANGELOG.md` and `docs/fr/CHANGELOG.md` for user-visible changes (Unreleased section) +5. Read [CONTRIBUTING.md](CONTRIBUTING.md) + +## Getting help + +- Open a GitHub Discussion or Issue (for bugs, use the issue template when available) +- Security issues: see [SECURITY.md](SECURITY.md) — do **not** open public issues for vulnerabilities diff --git a/docs/en/SECURITY.md b/docs/en/SECURITY.md new file mode 100644 index 0000000..8149a0e --- /dev/null +++ b/docs/en/SECURITY.md @@ -0,0 +1,36 @@ +# Security policy + +## Supported versions + +Security fixes are applied to the latest release on the default branch and backported at maintainers’ discretion. + +| Version | Supported | +| -------------- | ----------- | +| Latest release | Yes | +| Older releases | Best effort | + +## Reporting a vulnerability + +**Please do not open a public GitHub issue for security vulnerabilities.** + +Instead, email **contact@dyingstar-game.com** with: + +- A description of the issue and impact +- Steps to reproduce +- Affected versions or commits if known +- Any proof-of-concept (optional) + +We aim to acknowledge reports within a few business days and will coordinate disclosure after a fix is available. + +## Scope notes for this project + +- The launcher runs with elevated filesystem access for game install paths; only install to directories you trust. +- OAuth tokens are stored locally per environment; protect your user profile on shared machines. +- Variables prefixed with `VITE_` are embedded in the built application — never put secrets in `.env` with that prefix. +- Lore markdown is bundled locally; remote untrusted markdown is not loaded in production paths today. + +## Safe defaults for contributors + +- Keep `VITE_ENABLE_DEVTOOLS=false` in release builds +- Do not commit `.env`, tokens, or private keys +- Review IPC handlers for path traversal when touching filesystem code diff --git a/docs/fr/CHANGELOG.md b/docs/fr/CHANGELOG.md new file mode 100644 index 0000000..2de5cce --- /dev/null +++ b/docs/fr/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +Toutes les modifications notables du projet sont documentées ici. + +Format inspiré de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), +versionnage [Semantic Versioning](https://semver.org/lang/fr/). + +## [Non publié] + +### Ajouté + +- Documentation contributeur bilingue dans `docs/en/` et `docs/fr/` (onboarding, contribution, sécurité, code de conduite) +- Libellés de progression d’installation structurés et i18n UI (`installProgress.*`) +- Détection de la langue navigateur et persistance (`ds-language`) + +### Modifié + +- Gestionnaire de paquets **pnpm** uniquement (suppression de `package-lock.json`) +- Dialogue natif de dossier d’installation localisé (fr/en) côté main +- Mock `addFriend` : validation des doublons et erreurs typées + +### Supprimé + +- Stub IPC inutilisé `game:get-state` + +## [0.1.0] - 2025-01-01 + +### Ajouté + +- Premier launcher Electron open source : auth, install/mise à jour jeu, statut serveur, lore, UI social (mock) + +[Non publié]: https://github.com/DyingStar-game/launcher/compare/v0.1.0...develop +[0.1.0]: https://github.com/DyingStar-game/launcher/releases/tag/v0.1.0 diff --git a/docs/fr/CODE_OF_CONDUCT.md b/docs/fr/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4aed1fb --- /dev/null +++ b/docs/fr/CODE_OF_CONDUCT.md @@ -0,0 +1,32 @@ +# Code de conduite des contributeurs + +## Engagement + +Nous nous engageons à faire de la participation au projet Dying Star Launcher une expérience sans harcèlement pour tous, quels que soient l’âge, le corps, le handicap, l’ethnie, l’identité et l’expression de genre, le niveau d’expérience, la nationalité, l’apparence, la race, la religion ou l’orientation sexuelle. + +## Comportements attendus + +Exemples de comportements qui contribuent à un environnement positif : + +- Langage accueillant et inclusif +- Respect des points de vue et expériences différentes +- Acceptation constructive des critiques +- Priorité à l’intérêt de la communauté +- Empathie envers les autres membres + +Exemples de comportements inacceptables : + +- Trolling, insultes, attaques personnelles ou politiques +- Harcèlement public ou privé +- Publication d’informations privées sans consentement +- Tout comportement inapproprié dans un cadre professionnel + +## Application + +Les mainteneurs peuvent supprimer, modifier ou rejeter contributions, issues et commentaires non conformes, et exclure temporairement ou définitivement un contributeur. + +Les signalements peuvent être envoyés à **contact@dyingstar-game.com**. Ils seront traités de manière confidentielle et équitable. + +## Attribution + +Adapté du [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1. diff --git a/docs/fr/CONTRIBUTING.md b/docs/fr/CONTRIBUTING.md new file mode 100644 index 0000000..c06e293 --- /dev/null +++ b/docs/fr/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contribuer + +Merci de contribuer au Dying Star Launcher. + +## Liens utiles + +- [Onboarding](ONBOARDING.md) — architecture, installation, branches +- [Code de conduite](CODE_OF_CONDUCT.md) +- [Politique de sécurité](SECURITY.md) +- [Changelog](CHANGELOG.md) + +## Workflow + +1. Fork (contributeur externe) et clone +2. Se placer sur **`develop`**, créer une branche : `feature/description` ou `fix/description` +3. Installer avec **pnpm uniquement** : `pnpm install` +4. Copier `.env.example` → `.env` +5. Implémenter les changements +6. Vérifications : + ```bash + pnpm run format + pnpm run typecheck + pnpm run lint + ``` +7. Ouvrir une Pull Request **vers `develop`** + +## Conventions de code + +- **TypeScript** ; éviter `any` sans raison +- **Commentaires et JSDoc** en **anglais** sur les APIs et logique non triviale +- **Textes UI** uniquement via i18n (`src/renderer/i18n/*.json`) +- **IPC** : handler dans `src/main`, exposition dans `preload`, types dans `@shared/types` si partagés +- PRs courtes et ciblées + +## Messages de commit + +Sujets clairs à l’impératif, par ex. : + +- `feat(files): validate install path` +- `fix(auth): handle cancelled OAuth` +- `docs: onboarding pnpm` + +## Checklist PR + +- [ ] Basée sur `develop` à jour +- [ ] `pnpm run typecheck` OK +- [ ] `pnpm run lint` OK +- [ ] Changelog EN + FR mis à jour si changement visible +- [ ] Pas de secrets ni de `.env` commité +- [ ] Captures d’écran pour changements UI (optionnel) + +## Releases + +Les mainteneurs fusionnent `develop` → `main` et taguent. Voir le `README.md` racine pour les builds. + +## Licence + +En contribuant, tu acceptes la licence [MIT](../../LICENSE) du projet. diff --git a/docs/fr/ONBOARDING.md b/docs/fr/ONBOARDING.md new file mode 100644 index 0000000..c20b574 --- /dev/null +++ b/docs/fr/ONBOARDING.md @@ -0,0 +1,166 @@ +# Onboarding contributeur + +Bienvenue dans le dépôt **Dying Star Launcher**. Ce guide t’aide à comprendre le projet et à lancer un environnement de dev rapidement. + +## De quoi il s’agit + +Un **launcher Electron** pour le jeu _Dying Star_. Il gère notamment : + +- Authentification Discord / Keycloak (par environnement) +- Téléchargement, installation et mises à jour du jeu (ZIP via URLs configurables) +- Statut serveur et nombre de joueurs +- Vérification des mises à jour du launcher (GitHub Releases) +- Articles de lore (markdown embarqué) et une **UI social** (données mock pour l’instant) + +## Arborescence + +``` +src/ +├── main/ # Process Electron principal (IPC, auth, téléchargement, lancement jeu) +├── preload/ # API contextBridge exposée en window.api +├── renderer/ # UI React (vues, composants, stores Zustand, i18n) +└── shared/ # Types TypeScript partagés entre processus +docs/ # Documentation contributeur (en/ et fr/) +resources/ # Icônes et entitlements macOS +``` + +Alias (Vite/TS) : `@shared`, `@components`, `@views`, `@stores`, `@hooks`, `@lib`, `@i18n`, `@content`, `@assets`. + +## Branches + +| Branche | Rôle | +| ---------------- | ---------------------------------------------------------------------- | +| **`develop`** | **Branche par défaut pour le développement.** Ouvre tes PR ici. | +| `main` | Stable / alignée releases (merge depuis `develop` par les mainteneurs) | +| Branches feature | `feature/ton-sujet` depuis `develop` | + +```bash +git checkout develop +git pull origin develop +git checkout -b feature/ma-modif +``` + +## Prérequis + +- **Node.js 20+** +- **pnpm** (seul gestionnaire de paquets du dépôt — voir `packageManager` dans `package.json`) +- Git + +## Première installation + +```bash +git clone https://github.com/DyingStar-game/launcher.git +cd launcher +git checkout develop +pnpm install +cp .env.example .env +# Éditer .env — au minimum les URLs universe-testing pour tester l’install +pnpm run dev +``` + +### Fichiers d’environnement + +- Copier `.env.example` → `.env` (ne jamais committer `.env`) +- Les variables `VITE_*` sont incluses dans le binaire au build +- Pour travailler sur l’UI seule, les URLs de test dans l’exemple peuvent suffire + +### Scripts utiles + +| Commande | Description | +| -------------------- | ----------------------------- | +| `pnpm run dev` | Dev avec rechargement à chaud | +| `pnpm run typecheck` | TypeScript (main + renderer) | +| `pnpm run lint` | ESLint | +| `pnpm run format` | Prettier | +| `pnpm run build` | Build production dans `out/` | + +## ESLint et Prettier + +Le dépôt inclut **ESLint 9** (flat config) et **Prettier**, installés via `pnpm install` (pas d’installation globale nécessaire). + +### Fichiers de configuration + +| Fichier | Rôle | +| ------------------- | --------------------------------------------------------------------- | +| `eslint.config.mjs` | Config ESLint flat (TypeScript, React, Hooks, intégration Prettier) | +| `.prettierrc.yaml` | Règles Prettier (guillemets simples, sans point-virgule, largeur 100) | +| `.prettierignore` | Chemins exclus (`out/`, lockfiles, etc.) | + +`@electron-toolkit/eslint-config-prettier` évite les conflits : le formatage est géré par Prettier. + +### Commandes (racine du dépôt) + +```bash +# Vérifier le lint (obligatoire avant une PR) +pnpm run lint + +# Corriger automatiquement (y compris Prettier via eslint) +pnpm run lint:fix + +# Formater tout le projet +pnpm run format +``` + +Workflow recommandé avant commit : + +```bash +pnpm run format +pnpm run typecheck +pnpm run lint +``` + +### VS Code / Cursor + +Fichiers recommandés dans `.vscode/` : + +1. Extensions : + - **ESLint** (`dbaeumer.vscode-eslint`) + - **Prettier** (`esbenp.prettier-vscode`) +2. `.vscode/settings.json` : Prettier par défaut + format à l’enregistrement. + +Sinon : formateur par défaut **Prettier** + **Format On Save** activé. + +### CI + +À terme : `pnpm run typecheck` et `pnpm run lint` sur chaque PR vers `develop`. + +## Modèle mental de l’app + +1. Le **renderer** (React + Zustand) appelle **`window.api.*`** via le preload. +2. Le **preload** transmet via `ipcRenderer.invoke` vers les handlers **main**. +3. Le **main** fait le réseau, fichiers, OAuth, process jeu, et renvoie des événements (auth, progression install). + +La progression d’installation utilise des **clés structurées** (`InstallProgressLabel` dans `src/shared/types/installProgress.ts`) ; le renderer les traduit via i18next (`installProgress.*`). + +## Internationalisation (i18n) + +- Chaînes UI : `src/renderer/i18n/en.json` et `fr.json` +- `useTranslation()` + `t('clé')` dans les composants — pas de texte utilisateur en dur dans le TSX +- Langue : détection navigateur + `localStorage` (`ds-language`) ; drapeaux dans la navbar +- Dialogue dossier natif (main) : `src/main/l10n/dialogs.ts` (locale OS fr/en) + +## Points importants + +### Page Social (mock) + +`src/renderer/stores/social.ts` utilise des **données mock** en attendant les APIs. Les erreurs passent par `SocialStoreError` et `src/renderer/lib/socialErrors.ts`. + +### Deux environnements jeu + +- `universe` — production +- `universe-testing` — test + +Le sélecteur de la navbar change `useEnvStore().activeEnv` ; la plupart des stores sont indexés par `Env`. + +## Avant d’ouvrir une PR + +1. Partir de **`develop`** +2. `pnpm run typecheck && pnpm run lint` +3. PR focalisée, style cohérent avec le dépôt +4. Mettre à jour `docs/en/CHANGELOG.md` et `docs/fr/CHANGELOG.md` si changement visible utilisateur (section Unreleased) +5. Lire [CONTRIBUTING.md](CONTRIBUTING.md) + +## Aide + +- Discussion ou Issue GitHub +- Sécurité : [SECURITY.md](SECURITY.md) — **pas** d’issue publique pour une vulnérabilité diff --git a/docs/fr/SECURITY.md b/docs/fr/SECURITY.md new file mode 100644 index 0000000..3047e12 --- /dev/null +++ b/docs/fr/SECURITY.md @@ -0,0 +1,36 @@ +# Politique de sécurité + +## Versions prises en charge + +Les correctifs de sécurité sont appliqués sur la dernière release et la branche principale, avec backport au jugement des mainteneurs. + +| Version | Support | +| -------------------- | --------------- | +| Dernière release | Oui | +| Versions antérieures | Meilleur effort | + +## Signaler une vulnérabilité + +**Ne pas ouvrir d’issue GitHub publique pour une vulnérabilité.** + +Envoyer un e-mail à **contact@dyingstar-game.com** avec : + +- Description et impact +- Étapes de reproduction +- Versions ou commits concernés +- PoC éventuel + +Accusé de réception sous quelques jours ouvrés ; coordination de la divulgation après correctif. + +## Périmètre spécifique au launcher + +- Accès disque pour l’installation du jeu — n’installer que dans des dossiers de confiance. +- Jetons OAuth stockés localement par environnement — protéger le profil utilisateur sur machine partagée. +- Les variables `VITE_*` sont incluses dans le binaire — **jamais** de secrets avec ce préfixe. +- Le lore est du markdown embarqué ; pas de chargement markdown distant non fiable en prod aujourd’hui. + +## Bonnes pratiques contributeurs + +- `VITE_ENABLE_DEVTOOLS=false` pour les builds release +- Ne pas committer `.env`, jetons ou clés privées +- Vérifier le path traversal sur les handlers IPC touchant le filesystem diff --git a/eslint.config.mjs b/eslint.config.mjs index aff5d3f..4ffc272 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -25,7 +25,8 @@ export default defineConfig( }, rules: { ...eslintPluginReactHooks.configs.recommended.rules, - ...eslintPluginReactRefresh.configs.vite.rules + ...eslintPluginReactRefresh.configs.vite.rules, + 'react/prop-types': 'off' } }, eslintConfigPrettier diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 6e7dbd9..0000000 --- a/package-lock.json +++ /dev/null @@ -1,12257 +0,0 @@ -{ - "name": "dying-star-launcher", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "dying-star-launcher", - "version": "1.0.0", - "hasInstallScript": true, - "dependencies": { - "@electron-toolkit/preload": "^3.0.2", - "@electron-toolkit/utils": "^4.0.0", - "axios": "^1.16.0", - "debug": "^4.4.3", - "electron-store": "^11.0.2", - "electron-updater": "^6.3.9", - "extract-zip": "^2.0.1", - "get-stream": "^9.0.1", - "i18next": "^26.0.8", - "ms": "^2.1.3", - "openid-client": "^6.8.4", - "react-i18next": "^17.0.6", - "react-markdown": "^10.1.0", - "rehype-raw": "^7.0.0", - "rehype-sanitize": "^6.0.0", - "remark-gfm": "^4.0.1", - "yauzl": "^3.3.0", - "zustand": "^5.0.13" - }, - "devDependencies": { - "@electron-toolkit/eslint-config-prettier": "^3.0.0", - "@electron-toolkit/eslint-config-ts": "^3.1.0", - "@electron-toolkit/tsconfig": "^2.0.0", - "@tailwindcss/vite": "^4.2.4", - "@types/extract-zip": "^2.0.3", - "@types/node": "^22.19.1", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", - "autoprefixer": "^10.5.0", - "electron": "^39.2.6", - "electron-builder": "^26.0.12", - "electron-vite": "^5.0.0", - "eslint": "^9.39.1", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "prettier": "^3.7.4", - "react": "^19.2.1", - "react-dom": "^19.2.1", - "tailwindcss": "^4.2.4", - "typescript": "^5.9.3", - "vite": "^7.2.6" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/@electron-toolkit/eslint-config-prettier": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@electron-toolkit/eslint-config-prettier/-/eslint-config-prettier-3.0.0.tgz", - "integrity": "sha512-YapmIOVkbYdHLuTa+ad1SAVtcqYL9A/SJsc7cxQokmhcwAwonGevNom37jBf9slXegcZ/Slh01I/JARG1yhNFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.3" - }, - "peerDependencies": { - "eslint": ">= 9.0.0", - "prettier": ">= 3.0.0" - } - }, - "node_modules/@electron-toolkit/eslint-config-ts": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@electron-toolkit/eslint-config-ts/-/eslint-config-ts-3.1.0.tgz", - "integrity": "sha512-MowZQKd3yxXSDLack5QvjQwYHhpOJFoWBGBwJ/k+DCd7NUSendplECbQGFp86tPQYPUrPBPceR/hdsSAnaY5ZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint/js": "^9.24.0", - "globals": "^16.0.0", - "typescript-eslint": "^8.29.1" - }, - "peerDependencies": { - "eslint": ">=9.0.0", - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@electron-toolkit/preload": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@electron-toolkit/preload/-/preload-3.0.2.tgz", - "integrity": "sha512-TWWPToXd8qPRfSXwzf5KVhpXMfONaUuRAZJHsKthKgZR/+LqX1dZVSSClQ8OTAEduvLGdecljCsoT2jSshfoUg==", - "license": "MIT", - "peerDependencies": { - "electron": ">=13.0.0" - } - }, - "node_modules/@electron-toolkit/tsconfig": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@electron-toolkit/tsconfig/-/tsconfig-2.0.0.tgz", - "integrity": "sha512-AdPsP770WhW7b260h13SHMdmjEEHJL6xFtgi3jwgdsSQbJOkJLeNnnpZW9qxTPCvmRI6vmdzWz5K3gibFS6SNg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/node": "*" - } - }, - "node_modules/@electron-toolkit/utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@electron-toolkit/utils/-/utils-4.0.0.tgz", - "integrity": "sha512-qXSntwEzluSzKl4z5yFNBknmPGjPa3zFhE4mp9+h0cgokY5ornAeP+CJQDBhKsL1S58aOQfcwkD3NwLZCl+64g==", - "license": "MIT", - "peerDependencies": { - "electron": ">=13.0.0" - } - }, - "node_modules/@electron/asar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", - "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/@electron/asar/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@electron/asar/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@electron/fuses": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", - "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.1", - "fs-extra": "^9.0.1", - "minimist": "^1.2.5" - }, - "bin": { - "electron-fuses": "dist/bin.js" - } - }, - "node_modules/@electron/fuses/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/fuses/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/fuses/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@electron/notarize": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", - "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/osx-sign": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", - "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/rebuild": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", - "integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.1.1", - "node-abi": "^4.2.0", - "node-api-version": "^0.2.1", - "node-gyp": "^12.2.0", - "read-binary-file-arch": "^1.0.6" - }, - "bin": { - "electron-rebuild": "lib/cli.js" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@electron/universal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", - "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/asar": "^3.3.1", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "dir-compare": "^4.2.0", - "fs-extra": "^11.1.1", - "minimatch": "^9.0.3", - "plist": "^3.1.0" - }, - "engines": { - "node": ">=16.4" - } - }, - "node_modules/@electron/universal/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/universal/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", - "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/universal/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/windows-sign": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", - "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "cross-dirname": "^0.1.0", - "debug": "^4.3.4", - "fs-extra": "^11.1.1", - "minimist": "^1.2.8", - "postject": "^1.0.0-alpha.6" - }, - "bin": { - "electron-windows-sign": "bin/electron-windows-sign.js" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@electron/windows-sign/node_modules/fs-extra": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", - "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@electron/windows-sign/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/windows-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@malept/cross-spawn-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/@malept/flatpak-bundler": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.0", - "lodash": "^4.17.15", - "tmp-promise": "^3.0.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", - "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", - "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", - "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", - "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", - "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", - "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", - "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", - "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", - "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", - "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", - "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", - "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", - "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", - "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", - "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", - "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", - "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", - "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", - "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", - "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", - "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", - "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", - "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", - "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", - "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "license": "MIT" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", - "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.21.0", - "jiti": "^2.6.1", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.0" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", - "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-x64": "4.3.0", - "@tailwindcss/oxide-freebsd-x64": "4.3.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-x64-musl": "4.3.0", - "@tailwindcss/oxide-wasm32-wasi": "4.3.0", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", - "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", - "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", - "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", - "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", - "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", - "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", - "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", - "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", - "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", - "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", - "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.10.0", - "@emnapi/runtime": "^1.10.0", - "@emnapi/wasi-threads": "^1.2.1", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", - "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", - "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", - "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.0", - "@tailwindcss/oxide": "4.3.0", - "tailwindcss": "4.3.0" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/extract-zip": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/extract-zip/-/extract-zip-2.0.3.tgz", - "integrity": "sha512-yrO7h+0qOIGxHCmBeL5fKFzR+PBafh9LG6sOLBFFi2JuN+Hj663TAxfnqJh5vkQn963VimrhBF1GZzea3A+4Ig==", - "deprecated": "This is a stub types definition. extract-zip provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "extract-zip": "*" - } - }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz", - "integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/plist": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/verror": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", - "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/type-utils": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.59.2", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", - "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", - "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.2", - "@typescript-eslint/types": "^8.59.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", - "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", - "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", - "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", - "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", - "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.59.2", - "@typescript-eslint/tsconfig-utils": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", - "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", - "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.2", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", - "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/7zip-bin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/abbrev": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", - "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/app-builder-bin": { - "version": "5.0.0-alpha.12", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", - "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/app-builder-lib": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.8.1.tgz", - "integrity": "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@develar/schema-utils": "~2.6.5", - "@electron/asar": "3.4.1", - "@electron/fuses": "^1.8.0", - "@electron/get": "^3.0.0", - "@electron/notarize": "2.5.0", - "@electron/osx-sign": "1.3.3", - "@electron/rebuild": "^4.0.3", - "@electron/universal": "2.0.3", - "@malept/flatpak-bundler": "^0.4.0", - "@types/fs-extra": "9.0.13", - "async-exit-hook": "^2.0.1", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", - "chromium-pickle-js": "^0.2.0", - "ci-info": "4.3.1", - "debug": "^4.3.4", - "dotenv": "^16.4.5", - "dotenv-expand": "^11.0.6", - "ejs": "^3.1.8", - "electron-publish": "26.8.1", - "fs-extra": "^10.1.0", - "hosted-git-info": "^4.1.0", - "isbinaryfile": "^5.0.0", - "jiti": "^2.4.2", - "js-yaml": "^4.1.0", - "json5": "^2.2.3", - "lazy-val": "^1.0.5", - "minimatch": "^10.0.3", - "plist": "3.1.0", - "proper-lockfile": "^4.1.2", - "resedit": "^1.7.0", - "semver": "~7.7.3", - "tar": "^7.5.7", - "temp-file": "^3.4.0", - "tiny-async-pool": "1.3.0", - "which": "^5.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "dmg-builder": "26.8.1", - "electron-builder-squirrel-windows": "26.8.1" - } - }, - "node_modules/app-builder-lib/node_modules/@electron/get": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", - "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/app-builder-lib/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atomically": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz", - "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==", - "license": "MIT", - "dependencies": { - "stubborn-fs": "^2.0.0", - "when-exit": "^2.1.4" - } - }, - "node_modules/autoprefixer": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", - "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "caniuse-lite": "^1.0.30001787", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", - "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "optional": true - }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/builder-util": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.8.1.tgz", - "integrity": "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/debug": "^4.1.6", - "7zip-bin": "~5.2.0", - "app-builder-bin": "5.0.0-alpha.12", - "builder-util-runtime": "9.5.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.6", - "debug": "^4.3.4", - "fs-extra": "^10.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "js-yaml": "^4.1.0", - "sanitize-filename": "^1.6.3", - "source-map-support": "^0.5.19", - "stat-mode": "^1.0.0", - "temp-file": "^3.4.0", - "tiny-async-pool": "1.3.0" - } - }, - "node_modules/builder-util-runtime": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz", - "integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/builder-util/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/builder-util/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/builder-util/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/call-bind": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", - "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "get-intrinsic": "^1.3.0", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001792", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", - "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/chromium-pickle-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/conf": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-15.1.0.tgz", - "integrity": "sha512-Uy5YN9KEu0WWDaZAVJ5FAmZoaJt9rdK6kH+utItPyGsCqCgaTKkrmZx3zoE0/3q6S3bcp3Ihkk+ZqPxWxFK5og==", - "license": "MIT", - "dependencies": { - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "atomically": "^2.0.3", - "debounce-fn": "^6.0.0", - "dot-prop": "^10.0.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.7.2", - "uint8array-extras": "^1.5.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/conf/node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/conf/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } - }, - "node_modules/cross-dirname": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", - "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debounce-fn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-6.0.0.tgz", - "integrity": "sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT", - "optional": true - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dir-compare": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", - "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5", - "p-limit": "^3.1.0 " - } - }, - "node_modules/dir-compare/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/dmg-builder": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz", - "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", - "fs-extra": "^10.1.0", - "iconv-lite": "^0.6.2", - "js-yaml": "^4.1.0" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" - } - }, - "node_modules/dmg-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dmg-builder/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dot-prop": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz", - "integrity": "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==", - "license": "MIT", - "dependencies": { - "type-fest": "^5.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron": { - "version": "39.8.10", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.10.tgz", - "integrity": "sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^22.7.7", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 12.20.55" - } - }, - "node_modules/electron-builder": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.8.1.tgz", - "integrity": "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "dmg-builder": "26.8.1", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "simple-update-notifier": "2.0.0", - "yargs": "^17.6.2" - }, - "bin": { - "electron-builder": "cli.js", - "install-app-deps": "install-app-deps.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/electron-builder-squirrel-windows": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.8.1.tgz", - "integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", - "electron-winstaller": "5.4.0" - } - }, - "node_modules/electron-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-builder/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-publish": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.8.1.tgz", - "integrity": "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/fs-extra": "^9.0.11", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", - "chalk": "^4.1.2", - "form-data": "^4.0.5", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" - } - }, - "node_modules/electron-publish/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-publish/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-store": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-11.0.2.tgz", - "integrity": "sha512-4VkNRdN+BImL2KcCi41WvAYbh6zLX5AUTi4so68yPqiItjbgTjqpEnGAqasgnG+lB6GuAyUltKwVopp6Uv+gwQ==", - "license": "MIT", - "dependencies": { - "conf": "^15.0.2", - "type-fest": "^5.0.1" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.353", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", - "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", - "dev": true, - "license": "ISC" - }, - "node_modules/electron-updater": { - "version": "6.8.3", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.3.tgz", - "integrity": "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==", - "license": "MIT", - "dependencies": { - "builder-util-runtime": "9.5.1", - "fs-extra": "^10.1.0", - "js-yaml": "^4.1.0", - "lazy-val": "^1.0.5", - "lodash.escaperegexp": "^4.1.2", - "lodash.isequal": "^4.5.0", - "semver": "~7.7.3", - "tiny-typed-emitter": "^2.1.0" - } - }, - "node_modules/electron-updater/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-updater/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-updater/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-updater/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-vite": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/electron-vite/-/electron-vite-5.0.0.tgz", - "integrity": "sha512-OHp/vjdlubNlhNkPkL/+3JD34ii5ov7M0GpuXEVdQeqdQ3ulvVR7Dg/rNBLfS5XPIFwgoBLDf9sjjrL+CuDyRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.4", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "cac": "^6.7.14", - "esbuild": "^0.25.11", - "magic-string": "^0.30.19", - "picocolors": "^1.1.1" - }, - "bin": { - "electron-vite": "bin/electron-vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@swc/core": "^1.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - } - } - }, - "node_modules/electron-winstaller": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", - "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@electron/asar": "^3.2.1", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash": "^4.17.21", - "temp": "^0.9.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "@electron/windows-sign": "^1.1.2" - } - }, - "node_modules/electron-winstaller/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.21.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", - "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-abstract": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", - "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", - "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "license": "MIT", - "optional": true - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.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", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", - "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", - "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.26", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", - "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-plugin-react/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", - "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip/node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", - "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "license": "MIT", - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-sanitize": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", - "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "unist-util-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "license": "MIT", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/i18next": { - "version": "26.0.10", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.10.tgz", - "integrity": "sha512-k3yGPAlWR2RdMYoVXJoDZDT87qeHIWKH7gVksdZMpRty7QX/D9QZeYGvN08KGbKHke9wn01eYT+EEsrqX/YTlw==", - "funding": [ - { - "type": "individual", - "url": "https://www.locize.com/i18next" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - }, - { - "type": "individual", - "url": "https://www.locize.com" - } - ], - "license": "MIT", - "peerDependencies": { - "typescript": "^5 || ^6" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isbinaryfile": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", - "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jake": { - "version": "10.9.4", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", - "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.6", - "filelist": "^1.0.4", - "picocolors": "^1.1.1" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/jose": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC", - "optional": true - }, - "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/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lazy-val": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", - "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "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-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "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", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "license": "MIT", - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-abi": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.31.0.tgz", - "integrity": "sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.6.3" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-api-version": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", - "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - } - }, - "node_modules/node-api-version/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-gyp": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz", - "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "nopt": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "tar": "^7.5.4", - "tinyglobby": "^0.2.12", - "undici": "^6.25.0", - "which": "^6.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/node-gyp/node_modules/isexe": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=20" - } - }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^4.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", - "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^4.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/oauth4webapi": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", - "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/openid-client": { - "version": "6.8.4", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", - "integrity": "sha512-QSw0BA08piujetEwfZsHoTrDpMEha7GDZDicQqVwX4u0ChCjefvjDB++TZ8BTg76UpwhzIQgdvvfgfl3HpCSAw==", - "license": "MIT", - "dependencies": { - "jose": "^6.2.2", - "oauth4webapi": "^3.8.5" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/pe-library": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", - "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">=10.4.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postject": { - "version": "1.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", - "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "commander": "^9.4.0" - }, - "bin": { - "postject": "dist/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/postject/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", - "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", - "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.6" - } - }, - "node_modules/react-i18next": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.7.tgz", - "integrity": "sha512-rwtPXsb/zwzDafN+gytcjF5YnqGQQIRmCQ6DctBC1VSipRB8GD/MWEVrFP42vjMyuYydxWxM8CZRt+yiNuuoHg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "html-parse-stringify": "^3.0.1", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "i18next": ">= 26.0.10", - "react": ">= 16.8.0", - "typescript": "^5 || ^6" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-binary-file-arch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", - "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "bin": { - "read-binary-file-arch": "cli.js" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-sanitize": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", - "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-sanitize": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resedit": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", - "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pe-library": "^0.4.1" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" - } - }, - "node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/rollup": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", - "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.3", - "@rollup/rollup-android-arm64": "4.60.3", - "@rollup/rollup-darwin-arm64": "4.60.3", - "@rollup/rollup-darwin-x64": "4.60.3", - "@rollup/rollup-freebsd-arm64": "4.60.3", - "@rollup/rollup-freebsd-x64": "4.60.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", - "@rollup/rollup-linux-arm-musleabihf": "4.60.3", - "@rollup/rollup-linux-arm64-gnu": "4.60.3", - "@rollup/rollup-linux-arm64-musl": "4.60.3", - "@rollup/rollup-linux-loong64-gnu": "4.60.3", - "@rollup/rollup-linux-loong64-musl": "4.60.3", - "@rollup/rollup-linux-ppc64-gnu": "4.60.3", - "@rollup/rollup-linux-ppc64-musl": "4.60.3", - "@rollup/rollup-linux-riscv64-gnu": "4.60.3", - "@rollup/rollup-linux-riscv64-musl": "4.60.3", - "@rollup/rollup-linux-s390x-gnu": "4.60.3", - "@rollup/rollup-linux-x64-gnu": "4.60.3", - "@rollup/rollup-linux-x64-musl": "4.60.3", - "@rollup/rollup-openbsd-x64": "4.60.3", - "@rollup/rollup-openharmony-arm64": "4.60.3", - "@rollup/rollup-win32-arm64-msvc": "4.60.3", - "@rollup/rollup-win32-ia32-msvc": "4.60.3", - "@rollup/rollup-win32-x64-gnu": "4.60.3", - "@rollup/rollup-win32-x64-msvc": "4.60.3", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/safe-array-concat": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", - "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sanitize-filename": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", - "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", - "dev": true, - "license": "WTFPL OR ISC", - "dependencies": { - "truncate-utf8-bytes": "^1.0.0" - } - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "license": "MIT", - "optional": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "license": "MIT", - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "license": "(MIT OR CC0-1.0)", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/stat-mode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stubborn-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz", - "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==", - "license": "MIT", - "dependencies": { - "stubborn-utils": "^1.0.1" - } - }, - "node_modules/stubborn-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz", - "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==", - "license": "MIT" - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tagged-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", - "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", - "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "7.5.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", - "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp-file": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-exit-hook": "^2.0.1", - "fs-extra": "^10.0.0" - } - }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/temp-file/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/tiny-async-pool": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", - "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^5.5.0" - } - }, - "node_modules/tiny-async-pool/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tiny-typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tmp": "^0.2.0" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", - "dev": true, - "license": "WTFPL", - "dependencies": { - "utf8-byte-length": "^1.0.1" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz", - "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", - "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.2", - "@typescript-eslint/parser": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/uint8array-extras": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/utf8-byte-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "dev": true, - "license": "(WTFPL OR MIT)" - }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", - "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/when-exit": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz", - "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==", - "license": "MIT" - }, - "node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.0.tgz", - "integrity": "sha512-PtGEvEP30p7sbIBJKUBjUnqgTVOyMURc4dLo9iNyAJnNIEz9pm88cCXF21w94Kg3k6RXkeZh5DHOGS0qEONvNQ==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - }, - "node_modules/zustand": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.13.tgz", - "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/package.json b/package.json index bf6cc68..fe9c623 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,11 @@ { "name": "dying-star-launcher", "version": "0.1.0", + "packageManager": "pnpm@10.33.3", "description": "Launcher for DyingStar Game", + "engines": { + "node": ">=20" + }, "main": "./out/main/index.js", "author": { "name": "dyingstar-game.com", @@ -29,7 +33,11 @@ { "from": "resources/", "to": ".", - "filter": ["icon.ico", "icon.icns", "icon.png"] + "filter": [ + "icon.ico", + "icon.icns", + "icon.png" + ] } ], "win": { @@ -82,6 +90,7 @@ "scripts": { "format": "prettier --write .", "lint": "eslint --cache .", + "lint:fix": "eslint --cache . --fix", "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", "typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false", "typecheck": "npm run typecheck:node && npm run typecheck:web", diff --git a/src/main/config/api.ts b/src/main/config/api.ts index 7c15de3..053b695 100644 --- a/src/main/config/api.ts +++ b/src/main/config/api.ts @@ -1,27 +1,20 @@ -// Point d'accès unique aux variables d'environnement dans le main process. -// Avec electron-vite, les variables VITE_* sont injectées via import.meta.env. - import type { Env } from '@shared/types/env' /** - * Retourne l'URL de base de l'API pour un env donné. - * Retourne null si la variable est vide → env indisponible. + * Returns the API base URL for an environment, or null when unset. */ export function getApiBase(env: Env): string | null { - // import.meta.env.* est remplacé au build par electron-vite (fichiers .env / .env.production). - // process.env ne contient pas ces clés dans l’exe packagé. const raw = env === 'universe' ? import.meta.env.VITE_API_BASE_UNIVERSE : import.meta.env.VITE_API_BASE_TESTING - const trimmed = (raw ?? '').trim().replace(/\/$/, '') // supprime le slash final + const trimmed = (raw ?? '').trim().replace(/\/$/, '') return trimmed || null } /** - * URL utilisée uniquement pour télécharger les ZIP du jeu (optionnel). - * Si vide → on réutilise {@link getApiBase} (ex. CDN différent de l’API). + * Optional download host override; falls back to {@link getApiBase}. */ export function getDownloadBase(env: Env): string | null { const raw = @@ -33,6 +26,7 @@ export function getDownloadBase(env: Env): string | null { return trimmed || getApiBase(env) } +/** Picks the platform-specific ZIP URL from env overrides. */ function pickZipUrlForPlatform( platform: NodeJS.Platform, urls: { win: string; linux: string; darwin: string } @@ -43,39 +37,28 @@ function pickZipUrlForPlatform( return null } -/** - * URLs complètes des archives pour l’env **universe** (prod), une par OS. - * Si définie pour la plateforme courante, elle remplace `/game/latest-*.zip`. - */ +/** Full ZIP URL for production (`universe`) on the current platform. */ export function getUniverseZipUrlForPlatform(platform: NodeJS.Platform): string | null { return pickZipUrlForPlatform(platform, { - win: (import.meta.env.VITE_GAME_ZIP_WINDOWS ?? '').trim(), - linux: (import.meta.env.VITE_GAME_ZIP_LINUX ?? '').trim(), + win: (import.meta.env.VITE_GAME_ZIP_WINDOWS ?? '').trim(), + linux: (import.meta.env.VITE_GAME_ZIP_LINUX ?? '').trim(), darwin: (import.meta.env.VITE_GAME_ZIP_DARWIN ?? '').trim() }) } -/** - * URLs complètes des archives pour l’env **universe-testing** (une par OS). - * Si définie pour la plateforme courante, elle remplace `/game/latest-*.zip`. - */ +/** Full ZIP URL for testing (`universe-testing`) on the current platform. */ export function getTestingZipUrlForPlatform(platform: NodeJS.Platform): string | null { return pickZipUrlForPlatform(platform, { - win: (import.meta.env.VITE_GAME_ZIP_TESTING_WINDOWS ?? '').trim(), - linux: (import.meta.env.VITE_GAME_ZIP_TESTING_LINUX ?? '').trim(), + win: (import.meta.env.VITE_GAME_ZIP_TESTING_WINDOWS ?? '').trim(), + linux: (import.meta.env.VITE_GAME_ZIP_TESTING_LINUX ?? '').trim(), darwin: (import.meta.env.VITE_GAME_ZIP_TESTING_DARWIN ?? '').trim() }) } -/** - * Dérive l'URL de base du sous-domaine "status" depuis l'URL principale. - * https://dyingstar-game.com → https://status.dyingstar-game.com - * (sera transparent quand tout sera consolidé sous une seule URL) - */ +/** Derives the status subdomain from the main API base URL. */ export function getStatusBase(apiBase: string): string { try { const url = new URL(apiBase) - // Évite de doubler le préfixe si déjà présent if (!url.hostname.startsWith('status.')) { url.hostname = `status.${url.hostname}` } @@ -85,12 +68,13 @@ export function getStatusBase(apiBase: string): string { } } +/** Parses a positive Cachet component/metric id from env with fallback. */ function parseStatusNumericId(raw: string | undefined, fallback: number): number { const n = Number(String(raw ?? '').trim()) return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback } -/** ID Cachet du composant « statut » (API /api/components/:id) — par environnement. */ +/** Cachet component id for server status (`/api/components/:id`). */ export function getStatusComponentId(env: Env): number { const raw = env === 'universe' @@ -99,7 +83,7 @@ export function getStatusComponentId(env: Env): number { return parseStatusNumericId(raw, 3) } -/** ID Cachet de la métrique « joueurs » (API /api/metrics/:id/points) — par environnement. */ +/** Cachet metric id for player count (`/api/metrics/:id/points`). */ export function getStatusMetricId(env: Env): number { const raw = env === 'universe' @@ -108,17 +92,17 @@ export function getStatusMetricId(env: Env): number { return parseStatusNumericId(raw, 2) } -/** Tous les endpoints dérivés d'une URL de base */ +/** Endpoint builders derived from a base API URL. */ export const ENDPOINTS = { - version: (base: string): string => `${base}/version`, - status: (base: string, componentId: number): string => + version: (base: string): string => `${base}/version`, + status: (base: string, componentId: number): string => `${getStatusBase(base)}/api/components/${componentId}`, - metrics: (base: string, metricId: number): string => + metrics: (base: string, metricId: number): string => `${getStatusBase(base)}/api/metrics/${metricId}/points?sort=-id`, - zipWin32: (base: string): string => `${base}/game/latest-windows.zip`, - zipLinux: (base: string): string => `${base}/game/latest-linux.zip`, + zipWin32: (base: string): string => `${base}/game/latest-windows.zip`, + zipLinux: (base: string): string => `${base}/game/latest-linux.zip`, zipDarwin: (base: string): string => `${base}/game/latest-macos.zip`, - authBase: (base: string): string => { + authBase: (base: string): string => { try { const url = new URL(base) if (!url.hostname.startsWith('auth-preprod.') && !url.hostname.startsWith('auth.')) { @@ -129,4 +113,4 @@ export const ENDPOINTS = { return base } } -} \ No newline at end of file +} diff --git a/src/main/config/auth.ts b/src/main/config/auth.ts new file mode 100644 index 0000000..3d24dd4 --- /dev/null +++ b/src/main/config/auth.ts @@ -0,0 +1,19 @@ +import { APP_PROTOCOL } from './constants' + +/** OAuth2 / OpenID client id registered in Keycloak. */ +export const AUTH_CLIENT_ID = + (import.meta.env.VITE_AUTH_CLIENT_ID ?? 'dyingstar-launcher').trim() || 'dyingstar-launcher' + +/** Keycloak realm name. */ +export const AUTH_REALM = (import.meta.env.VITE_AUTH_REALM ?? 'dyingstar').trim() || 'dyingstar' + +/** Redirect URI registered for the desktop OAuth flow. */ +export const AUTH_REDIRECT_URI = + (import.meta.env.VITE_AUTH_REDIRECT_URI ?? `${APP_PROTOCOL}://auth/callback`).trim() || + `${APP_PROTOCOL}://auth/callback` + +/** OpenID scopes requested during login and refresh. */ +export const AUTH_SCOPES = 'openid email profile' + +/** Identity provider hint passed to Keycloak (Discord). */ +export const AUTH_IDP_HINT = 'discord' diff --git a/src/main/config/constants.ts b/src/main/config/constants.ts new file mode 100644 index 0000000..bf1bbae --- /dev/null +++ b/src/main/config/constants.ts @@ -0,0 +1,48 @@ +/** Parses a positive integer from an env string with a numeric fallback. */ +export function parsePositiveInt(raw: string | undefined, fallback: number): number { + const n = Number(raw) + return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback +} + +/** HTTP / fetch timeouts (milliseconds). Overridable via VITE_* env vars. */ +export const HTTP_TIMEOUT_MS = { + api: parsePositiveInt(import.meta.env.VITE_HTTP_TIMEOUT_API_MS, 8_000), + versionPing: parsePositiveInt(import.meta.env.VITE_HTTP_TIMEOUT_VERSION_PING_MS, 5_000), + github: parsePositiveInt(import.meta.env.VITE_HTTP_TIMEOUT_GITHUB_MS, 10_000), + download: parsePositiveInt(import.meta.env.VITE_DOWNLOAD_TIMEOUT_MS, 600_000) +} as const + +/** Game process poll interval when tracking a running child (milliseconds). */ +export const GAME_PROCESS_POLL_MS = parsePositiveInt( + import.meta.env.VITE_GAME_PROCESS_POLL_MS, + 2_000 +) + +/** Install layout: subdirectory name inside the user-chosen install path. */ +export const GAME_INSTALL_SUBDIR = + (import.meta.env.VITE_GAME_INSTALL_SUBDIR ?? 'DyingStar').trim() || 'DyingStar' + +/** Temporary ZIP filename during download. */ +export const GAME_DOWNLOAD_ZIP_NAME = '__game_download.zip' + +/** Platform-specific game executable names (relative to game root). */ +export const GAME_EXECUTABLES: Partial> = { + win32: (import.meta.env.VITE_GAME_EXE_WINDOWS ?? 'DyingStar.exe').trim() || 'DyingStar.exe', + linux: (import.meta.env.VITE_GAME_EXE_LINUX ?? 'DyingStar.x86_64').trim() || 'DyingStar.x86_64', + darwin: (import.meta.env.VITE_GAME_EXE_DARWIN ?? 'DyingStar.app').trim() || 'DyingStar.app' +} + +/** Custom protocol scheme for OAuth callbacks. */ +export const APP_PROTOCOL = (import.meta.env.VITE_APP_PROTOCOL ?? 'dyingstar').trim() || 'dyingstar' + +/** User-Agent sent when downloading game archives. */ +export const DOWNLOAD_USER_AGENT = + (import.meta.env.VITE_DOWNLOAD_USER_AGENT ?? 'DyingStar-Launcher/1.0').trim() || + 'DyingStar-Launcher/1.0' + +/** Download progress phase weights (0–100 scale). */ +export const DOWNLOAD_PROGRESS = { + zipWeight: 70, + unknownChunkBytes: 2 * 1024 * 1024, + unknownSizeCapBytes: 80 * 1024 * 1024 +} as const diff --git a/src/main/icon.ts b/src/main/icon.ts index e4c6a72..753d2aa 100644 --- a/src/main/icon.ts +++ b/src/main/icon.ts @@ -3,29 +3,26 @@ import { existsSync } from 'fs' import { join } from 'path' import { is } from '@electron-toolkit/utils' -/** Nom du fichier icône selon la plateforme. */ +/** Icon file name for the current platform. */ function iconFileName(): string { if (process.platform === 'win32') return 'icon.ico' if (process.platform === 'darwin') return 'icon.icns' return 'icon.png' } -/** Chemins candidats (dev electron-vite, preview, build packagé). */ +/** Candidate icon paths (dev, preview, packaged build). */ function candidateIconPaths(): string[] { const name = iconFileName() const paths: string[] = [] - // Dev / preview : `out/main` → `../../resources` paths.push(join(__dirname, '../../resources', name)) - // Racine du projet (app.getAppPath() en dev) try { paths.push(join(app.getAppPath(), 'resources', name)) } catch { - /* app pas encore prêt */ + /* app not ready yet */ } - // Binaire packagé (extraResources) if (process.resourcesPath) { paths.push(join(process.resourcesPath, name)) } @@ -33,7 +30,7 @@ function candidateIconPaths(): string[] { return paths } -/** Chemin absolu vers l’icône du launcher, ou null si introuvable. */ +/** Absolute path to the launcher icon, or null when not found. */ export function resolveAppIconPath(): string | null { for (const p of candidateIconPaths()) { if (existsSync(p)) return p @@ -41,11 +38,12 @@ export function resolveAppIconPath(): string | null { return null } +/** Loads the native image for BrowserWindow / tray use. */ export function loadAppIcon(): Electron.NativeImage | undefined { const iconPath = resolveAppIconPath() if (!iconPath) { if (is.dev) { - console.warn('[Icon] Fichier icône introuvable — barre des tâches Electron par défaut.') + console.warn('[Icon] Icon file not found — using default Electron taskbar icon.') } return undefined } @@ -53,7 +51,7 @@ export function loadAppIcon(): Electron.NativeImage | undefined { return image.isEmpty() ? undefined : image } -/** Icône dock macOS + cohérence globale. */ +/** Applies the icon to the macOS dock when available. */ export function applyAppIcon(): void { const image = loadAppIcon() if (!image) return diff --git a/src/main/index.ts b/src/main/index.ts index 806b0aa..f58ce8b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,8 +1,6 @@ import { app, shell, BrowserWindow, ipcMain } from 'electron' import { join } from 'path' import path from 'path' -import fs from 'fs' -import { execSync } from 'child_process' import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { applyAppIcon, loadAppIcon } from './icon' import { registerFilesHandlers } from './services/game' @@ -10,89 +8,36 @@ import { registerVersionHandlers } from './services/version' import { registerGameStatusHandlers } from './services/gameStatus' import { registerAuthHandlers, handleOAuthCallback } from './services/auth' import { registerWindowHandlers, MIN_WIDTH, MIN_HEIGHT } from './services/window' +import { APP_PROTOCOL, parsePositiveInt } from './config/constants' +import { registerLinuxProtocolHandler } from './protocol/linuxDesktop' import log from 'electron-log' -// ─── Manage log (import.meta.env = valeurs figées au build, comme les autres VITE_*) ─── -if (import.meta.env.VITE_ELECTRON_ENABLE_LOGGING === 'true') log.transports.file.level = 'debug' - -function parsePositiveInt(raw: string | undefined, fallback: number): number { - const n = Number(raw) - return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback +if (import.meta.env.VITE_ELECTRON_ENABLE_LOGGING === 'true') { + log.transports.file.level = 'debug' } -// ─── Custom protocol (must be set before app is ready) ──────────────────────── -// In development, Electron is launched as `electron /path/to/main.js`; the -// protocol client must include the script path so the OS routes the callback -// back to this instance via a second-instance launch. - +/** Register custom protocol before app ready (dev needs script path in argv). */ if (process.defaultApp && process.argv.length >= 2) { - app.setAsDefaultProtocolClient('dyingstar', process.execPath, [path.resolve(process.argv[1])]) + app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [path.resolve(process.argv[1])]) } else { - app.setAsDefaultProtocolClient('dyingstar') -} - -// ─── Linux: ensure xdg protocol handler is registered ──────────────────────── -// Electron's setAsDefaultProtocolClient does not create the .desktop file on -// Linux in dev mode. We do it manually so the OS can route dyingstar:// back. - -function registerLinuxProtocol(): void { - if (process.platform !== 'linux') return - - const desktopDir = path.join(app.getPath('home'), '.local', 'share', 'applications') - const desktopFile = path.join(desktopDir, 'dyingstar-launcher.desktop') - - const execLine = process.defaultApp && process.argv.length >= 2 - ? `${process.execPath} ${path.resolve(process.argv[1])} %U` - : `${process.execPath} %U` - - const contents = [ - '[Desktop Entry]', - 'Type=Application', - 'Name=DyingStar Launcher', - `Exec=${execLine}`, - 'MimeType=x-scheme-handler/dyingstar;', - 'NoDisplay=true', - ].join('\n') + '\n' - - try { - fs.mkdirSync(desktopDir, { recursive: true }) - fs.writeFileSync(desktopFile, contents, 'utf-8') - - try { - execSync(`xdg-mime default dyingstar-launcher.desktop x-scheme-handler/dyingstar`) - } catch { - console.warn('[Protocol] xdg-mime not available, skipping mime association') - } - - try { - execSync(`update-desktop-database ${desktopDir}`) - } catch { - console.warn('[Protocol] update-desktop-database not available, skipping db update') - } - - console.log('[Protocol] dyingstar:// registered via xdg') - } catch (err) { - console.error('[Protocol] Failed to register dyingstar:// handler:', err) - } + app.setAsDefaultProtocolClient(APP_PROTOCOL) } -// ─── macOS / Linux: app already running, URL opened externally ──────────────── - +/** macOS: OAuth callback URL opened while app is running. */ app.on('open-url', (_event, url) => { - handleOAuthCallback(url) + void handleOAuthCallback(url) }) -// ─── Windows: second instance spawned with the URL as argv ─────────────────── - let mainWindow: BrowserWindow | null = null const gotLock = app.requestSingleInstanceLock() if (!gotLock) { app.quit() } else { + /** Windows: second instance receives OAuth URL in argv. */ app.on('second-instance', (_event, argv) => { - const url = argv.find((a) => a.startsWith('dyingstar://')) - if (url) handleOAuthCallback(url) + const url = argv.find((a) => a.startsWith(`${APP_PROTOCOL}://`)) + if (url) void handleOAuthCallback(url) if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() @@ -100,13 +45,14 @@ if (!gotLock) { }) } +/** Creates the main application window and registers IPC handlers bound to it. */ function createWindow(): void { const appIcon = loadAppIcon() mainWindow = new BrowserWindow({ - width: parsePositiveInt(import.meta.env.VITE_WINDOW_WIDTH, 1200), + width: parsePositiveInt(import.meta.env.VITE_WINDOW_WIDTH, 1200), height: parsePositiveInt(import.meta.env.VITE_WINDOW_HEIGHT, 800), - minWidth: MIN_WIDTH, + minWidth: MIN_WIDTH, minHeight: MIN_HEIGHT, frame: false, backgroundColor: '#0d0d14', @@ -121,14 +67,14 @@ function createWindow(): void { }) registerWindowHandlers(() => mainWindow) - - // Enregistrement des handlers IPC registerFilesHandlers(mainWindow) registerAuthHandlers(mainWindow) mainWindow.on('ready-to-show', () => { mainWindow!.show() - if (import.meta.env.VITE_ENABLE_DEVTOOLS === 'true') mainWindow!.webContents.openDevTools() + if (import.meta.env.VITE_ENABLE_DEVTOOLS === 'true') { + mainWindow!.webContents.openDevTools() + } }) mainWindow.webContents.setWindowOpenHandler((details) => { @@ -147,8 +93,7 @@ app.whenReady().then(() => { electronApp.setAppUserModelId('com.dyingstar.launcher') applyAppIcon() - - registerLinuxProtocol() + registerLinuxProtocolHandler() app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window) @@ -159,13 +104,11 @@ app.whenReady().then(() => { app.quit() }) - // Handlers indépendants de la fenêtre registerVersionHandlers() registerGameStatusHandlers() - createWindow() - app.on('activate', function () { + app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) @@ -174,4 +117,4 @@ app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } -}) \ No newline at end of file +}) diff --git a/src/main/l10n/dialogs.ts b/src/main/l10n/dialogs.ts new file mode 100644 index 0000000..0b4b43d --- /dev/null +++ b/src/main/l10n/dialogs.ts @@ -0,0 +1,27 @@ +import { app } from 'electron' + +type InstallDialogStrings = { + selectDirectoryTitle: string + selectDirectoryButton: string +} + +const DIALOGS: Record<'en' | 'fr', InstallDialogStrings> = { + en: { + selectDirectoryTitle: 'Installation directory', + selectDirectoryButton: 'Select folder' + }, + fr: { + selectDirectoryTitle: "Répertoire d'installation", + selectDirectoryButton: 'Choisir le dossier' + } +} + +function resolveLocale(): 'en' | 'fr' { + const locale = app.getLocale().toLowerCase() + return locale.startsWith('fr') ? 'fr' : 'en' +} + +/** Native open-dialog strings aligned with the OS locale (fr/en). */ +export function getInstallDialogStrings(): InstallDialogStrings { + return DIALOGS[resolveLocale()] +} diff --git a/src/main/protocol/linuxDesktop.ts b/src/main/protocol/linuxDesktop.ts new file mode 100644 index 0000000..6c08341 --- /dev/null +++ b/src/main/protocol/linuxDesktop.ts @@ -0,0 +1,49 @@ +import { app } from 'electron' +import { execSync } from 'child_process' +import fs from 'fs' +import path from 'path' +import { APP_PROTOCOL } from '../config/constants' + +/** Registers a `.desktop` file so Linux can route custom protocol URLs to this app. */ +export function registerLinuxProtocolHandler(): void { + if (process.platform !== 'linux') return + + const desktopDir = path.join(app.getPath('home'), '.local', 'share', 'applications') + const desktopFile = path.join(desktopDir, 'dyingstar-launcher.desktop') + + const execLine = + process.defaultApp && process.argv.length >= 2 + ? `${process.execPath} ${path.resolve(process.argv[1])} %U` + : `${process.execPath} %U` + + const contents = + [ + '[Desktop Entry]', + 'Type=Application', + 'Name=DyingStar Launcher', + `Exec=${execLine}`, + `MimeType=x-scheme-handler/${APP_PROTOCOL};`, + 'NoDisplay=true' + ].join('\n') + '\n' + + try { + fs.mkdirSync(desktopDir, { recursive: true }) + fs.writeFileSync(desktopFile, contents, 'utf-8') + + try { + execSync(`xdg-mime default dyingstar-launcher.desktop x-scheme-handler/${APP_PROTOCOL}`) + } catch { + console.warn('[Protocol] xdg-mime not available, skipping mime association') + } + + try { + execSync(`update-desktop-database ${desktopDir}`) + } catch { + console.warn('[Protocol] update-desktop-database not available, skipping db update') + } + + console.log(`[Protocol] ${APP_PROTOCOL}:// registered via xdg`) + } catch (err) { + console.error('[Protocol] Failed to register custom protocol handler:', err) + } +} diff --git a/src/main/services/auth.ts b/src/main/services/auth.ts deleted file mode 100644 index a6cf546..0000000 --- a/src/main/services/auth.ts +++ /dev/null @@ -1,356 +0,0 @@ -import { app, ipcMain, shell, safeStorage, BrowserWindow } from 'electron' -import crypto from 'crypto' -import fs from 'fs' -import path from 'path' -import { URL } from 'url' -import { getApiBase, ENDPOINTS } from '../config/api' -import type { Env } from '@shared/types/env' -import type { UserInfo } from '@shared/types/auth' - -export type { UserInfo } from '@shared/types/auth' - -// ─── Types ──────────────────────────────────────────────────────────────────── - -interface TokenSet { - access_token: string - refresh_token: string - id_token: string - expires_in: number - token_type: string -} - -interface PKCESession { - env: Env - verifier: string - state: string -} - -// ─── State ──────────────────────────────────────────────────────────────────── - -const REDIRECT_URI = 'dyingstar://auth/callback' -const CLIENT_ID = 'dyingstar-launcher' -const REALM = 'dyingstar' - -let pkceSession: PKCESession | null = null -let mainWindow: BrowserWindow | null = null - -// ─── Auth base dérivé de l'API base ─────────────────────────────────────────── - -function getKeycloakBase(env: Env): string | null { - const base = getApiBase(env) - if (!base) return null - return ENDPOINTS.authBase(base) -} - -// ─── Token Storage ──────────────────────────────────────────────────────────── - -function getTokenPath(env: Env): string { - const filename = env === 'universe' ? 'tokens.enc' : `tokens-${env}.enc` - return path.join(app.getPath('userData'), filename) -} - -function storeTokens(env: Env, tokens: TokenSet): void { - const json = JSON.stringify(tokens) - if (safeStorage.isEncryptionAvailable()) { - fs.writeFileSync(getTokenPath(env), safeStorage.encryptString(json)) - } else { - console.warn('[Auth] safeStorage unavailable, plain JSON fallback') - fs.writeFileSync(getTokenPath(env), json, 'utf-8') - } -} - -function loadTokens(env: Env): TokenSet | null { - const p = getTokenPath(env) - if (!fs.existsSync(p)) return null - try { - const buf = fs.readFileSync(p) - if (safeStorage.isEncryptionAvailable()) { - return JSON.parse(safeStorage.decryptString(buf)) as TokenSet - } - return JSON.parse(buf.toString('utf-8')) as TokenSet - } catch { return null } -} - -function clearStoredTokens(env: Env): void { - const p = getTokenPath(env) - if (fs.existsSync(p)) fs.unlinkSync(p) -} - -// ─── PKCE ───────────────────────────────────────────────────────────────────── - -function generatePKCE(): { verifier: string; challenge: string } { - const verifier = crypto.randomBytes(32).toString('base64url') - const challenge = crypto.createHash('sha256').update(verifier).digest('base64url') - return { verifier, challenge } -} - -function generateState(): string { - return crypto.randomBytes(16).toString('base64url') -} - -// ─── JWT ────────────────────────────────────────────────────────────────────── - -function decodeJwtPayload(token: string): Record { - const parts = token.split('.') - if (parts.length !== 3) throw new Error('Invalid JWT') - return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8')) -} - -const UUID_RE = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i - -function getJwtSub(token: string): string | null { - try { - const sub = decodeJwtPayload(token)['sub'] - return typeof sub === 'string' && sub.length > 0 ? sub : null - } catch { - return null - } -} - -function isValidPlayerSub(sub: string): boolean { - return UUID_RE.test(sub) -} - -function isJwtNotExpired(token: string): boolean { - try { - const exp = decodeJwtPayload(token)['exp'] - if (typeof exp !== 'number') return false - return exp > Math.floor(Date.now() / 1000) - } catch { - return false - } -} - -function getJwtPreferredUsername(token: string): string | null { - try { - const username = decodeJwtPayload(token)['preferred_username'] - return typeof username === 'string' && username.length > 0 ? username : null - } catch { - return null - } -} - -function isValidGameLaunchToken(token: string): boolean { - const sub = getJwtSub(token) - return ( - sub !== null && - isValidPlayerSub(sub) && - getJwtPreferredUsername(token) !== null && - isJwtNotExpired(token) - ) -} - -/** Choisit l'id_token pour le client jeu (claims identité requis côté serveur). */ -function pickGameLaunchToken(tokens: TokenSet): string | null { - const token = tokens.id_token - if (!token || !isValidGameLaunchToken(token)) return null - return token -} - -function extractUser(tokens: TokenSet): UserInfo { - const p = decodeJwtPayload(tokens.id_token) - return { - sub: p['sub'] as string, - username: ((p['preferred_username'] ?? p['name']) as string | undefined) ?? 'Unknown', - email: (p['email'] as string | undefined) ?? '' - } -} - -// ─── Auth Flow ──────────────────────────────────────────────────────────────── - -export async function startLogin(env: Env): Promise { - const kcBase = getKeycloakBase(env) - if (!kcBase) throw new Error(`Auth non disponible pour l'env: ${env}`) - - const { verifier, challenge } = generatePKCE() - const state = generateState() - pkceSession = { env, verifier, state } - - const params = new URLSearchParams({ - client_id: CLIENT_ID, - redirect_uri: REDIRECT_URI, - response_type: 'code', - scope: 'openid email profile', - kc_idp_hint: 'discord', - code_challenge: challenge, - code_challenge_method: 'S256', - state - }) - - await shell.openExternal( - `${kcBase}/realms/${REALM}/protocol/openid-connect/auth?${params}` - ) -} - -export async function handleOAuthCallback(rawUrl: string): Promise { - let url: URL - try { url = new URL(rawUrl) } catch { return } - if (url.protocol !== 'dyingstar:' || url.host !== 'auth' || url.pathname !== '/callback') return - - const code = url.searchParams.get('code') - const state = url.searchParams.get('state') - const error = url.searchParams.get('error') - - if (!code && !error) return // post-logout - - const envForEvent = pkceSession?.env ?? 'universe-testing' - - if (error) { - mainWindow?.webContents.send('auth:state-changed', { env: envForEvent, status: 'error', error }) - return - } - - if (!code || !state || !pkceSession || state !== pkceSession.state) { - mainWindow?.webContents.send('auth:state-changed', { - env: envForEvent, status: 'error', error: 'invalid_callback' - }) - return - } - - const { env, verifier } = pkceSession - pkceSession = null - - const kcBase = getKeycloakBase(env) - if (!kcBase) return - - try { - const tokens = await exchangeCode(kcBase, code, verifier) - storeTokens(env, tokens) - mainWindow?.webContents.send('auth:state-changed', { - env, status: 'connected', user: extractUser(tokens) - }) - } catch (err) { - console.error('[Auth] Token exchange failed:', err) - mainWindow?.webContents.send('auth:state-changed', { - env, status: 'error', error: 'token_exchange_failed' - }) - } -} - -async function exchangeCode(kcBase: string, code: string, verifier: string): Promise { - const res = await fetch( - `${kcBase}/realms/${REALM}/protocol/openid-connect/token`, - { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - grant_type: 'authorization_code', - client_id: CLIENT_ID, - code, - redirect_uri: REDIRECT_URI, - code_verifier: verifier - }).toString() - } - ) - if (!res.ok) throw new Error(`Token exchange failed (${res.status}): ${await res.text()}`) - return res.json() as Promise -} - -async function refreshToken(kcBase: string, token: string): Promise { - const res = await fetch( - `${kcBase}/realms/${REALM}/protocol/openid-connect/token`, - { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - grant_type: 'refresh_token', - client_id: CLIENT_ID, - refresh_token: token, - scope: 'openid email profile' - }).toString() - } - ) - if (!res.ok) return null - return res.json() as Promise -} - -function mergeTokenSet(previous: TokenSet, refreshed: TokenSet): TokenSet { - const idCandidates = [refreshed.id_token, previous.id_token].filter(Boolean) - const id_token = - idCandidates.find((t) => isValidGameLaunchToken(t)) ?? - refreshed.id_token ?? - previous.id_token - - return { ...previous, ...refreshed, id_token } -} - -/** JWT valide pour le client jeu (rafraîchit si besoin, sub UUID requis). */ -export async function loadFreshGameToken(env: Env): Promise { - const kcBase = getKeycloakBase(env) - const tokens = loadTokens(env) - if (!kcBase || !tokens) return null - - const refreshed = await refreshToken(kcBase, tokens.refresh_token) - if (!refreshed) { - clearStoredTokens(env) - return null - } - - const merged = mergeTokenSet(tokens, refreshed) - storeTokens(env, merged) - const launchToken = pickGameLaunchToken(merged) - if (!launchToken) { - console.error('[Auth] Jeton jeu invalide:', describeGameTokenIssue(merged)) - } - return launchToken -} - -function describeGameTokenIssue(tokens: TokenSet): string { - const token = tokens.id_token - if (!token) return 'id_token absent — reconnectez-vous' - if (!isJwtNotExpired(token)) return 'id_token expiré — reconnectez-vous' - const sub = getJwtSub(token) - if (!sub || !isValidPlayerSub(sub)) return `sub UUID invalide (${sub ?? 'vide'})` - if (!getJwtPreferredUsername(token)) { - return 'preferred_username absent de l\'id_token (vérifier Keycloak / scope profile)' - } - return 'raison inconnue' -} - -// ─── IPC Handlers ───────────────────────────────────────────────────────────── - -export function registerAuthHandlers(win: BrowserWindow): void { - mainWindow = win - - ipcMain.handle('auth:login', async (_event, env: Env) => { - try { - await startLogin(env) - } catch (err) { - console.error('[Auth] Failed to open browser:', err) - mainWindow?.webContents.send('auth:state-changed', { - env, status: 'error', error: 'browser_open_failed' - }) - } - }) - - ipcMain.handle('auth:logout', (_event, env: Env) => { - const kcBase = getKeycloakBase(env) - const tokens = loadTokens(env) - clearStoredTokens(env) - - if (kcBase && tokens?.id_token) { - const params = new URLSearchParams({ - client_id: CLIENT_ID, - post_logout_redirect_uri: REDIRECT_URI, - id_token_hint: tokens.id_token - }) - shell.openExternal( - `${kcBase}/realms/${REALM}/protocol/openid-connect/logout?${params}` - ) - } - }) - - ipcMain.handle('auth:load-user', async (_event, env: Env): Promise => { - const kcBase = getKeycloakBase(env) - const tokens = loadTokens(env) - if (!kcBase || !tokens) return null - - const refreshed = await refreshToken(kcBase, tokens.refresh_token) - if (!refreshed) { clearStoredTokens(env); return null } - - const merged = mergeTokenSet(tokens, refreshed) - storeTokens(env, merged) - return extractUser(merged) - }) -} \ No newline at end of file diff --git a/src/main/services/auth/flow.ts b/src/main/services/auth/flow.ts new file mode 100644 index 0000000..44d0383 --- /dev/null +++ b/src/main/services/auth/flow.ts @@ -0,0 +1,168 @@ +import { shell } from 'electron' +import { URL, URLSearchParams } from 'url' +import { + AUTH_CLIENT_ID, + AUTH_IDP_HINT, + AUTH_REALM, + AUTH_REDIRECT_URI, + AUTH_SCOPES +} from '../../config/auth' +import { APP_PROTOCOL } from '../../config/constants' +import type { Env } from '@shared/types/env' +import type { UserInfo } from '@shared/types/auth' +import type { PKCESession } from './types' +import { generatePKCE, generateState } from './pkce' +import { describeGameTokenIssue, extractUser, pickGameLaunchToken } from './jwt' +import { exchangeCode, getKeycloakBase, mergeTokenSet, refreshToken } from './keycloakClient' +import { clearStoredTokens, loadTokens, storeTokens } from './tokenStorage' + +let pkceSession: PKCESession | null = null + +type AuthEventSender = { + send(channel: 'auth:state-changed', payload: unknown): void +} + +let eventSender: AuthEventSender | null = null + +/** Registers the object used to push auth state events to the renderer. */ +export function setAuthEventSender(sender: AuthEventSender): void { + eventSender = sender +} + +/** Opens the system browser on the Keycloak authorization URL (PKCE). */ +export async function startLogin(env: Env): Promise { + const kcBase = getKeycloakBase(env) + if (!kcBase) throw new Error(`Auth unavailable for env: ${env}`) + + const { verifier, challenge } = generatePKCE() + const state = generateState() + pkceSession = { env, verifier, state } + + const params = new URLSearchParams({ + client_id: AUTH_CLIENT_ID, + redirect_uri: AUTH_REDIRECT_URI, + response_type: 'code', + scope: AUTH_SCOPES, + kc_idp_hint: AUTH_IDP_HINT, + code_challenge: challenge, + code_challenge_method: 'S256', + state + }) + + await shell.openExternal(`${kcBase}/realms/${AUTH_REALM}/protocol/openid-connect/auth?${params}`) +} + +/** Handles `dyingstar://auth/callback` deep links after OAuth redirect. */ +export async function handleOAuthCallback(rawUrl: string): Promise { + let url: URL + try { + url = new URL(rawUrl) + } catch { + return + } + + if (url.protocol !== `${APP_PROTOCOL}:` || url.host !== 'auth' || url.pathname !== '/callback') { + return + } + + const code = url.searchParams.get('code') + const state = url.searchParams.get('state') + const error = url.searchParams.get('error') + + if (!code && !error) return + + const envForEvent = pkceSession?.env ?? 'universe-testing' + + if (error) { + eventSender?.send('auth:state-changed', { env: envForEvent, status: 'error', error }) + return + } + + if (!code || !state || !pkceSession || state !== pkceSession.state) { + eventSender?.send('auth:state-changed', { + env: envForEvent, + status: 'error', + error: 'invalid_callback' + }) + return + } + + const { env, verifier } = pkceSession + pkceSession = null + + const kcBase = getKeycloakBase(env) + if (!kcBase) return + + try { + const tokens = await exchangeCode(kcBase, code, verifier) + storeTokens(env, tokens) + eventSender?.send('auth:state-changed', { + env, + status: 'connected', + user: extractUser(tokens) + }) + } catch (err) { + console.error('[Auth] Token exchange failed:', err) + eventSender?.send('auth:state-changed', { + env, + status: 'error', + error: 'token_exchange_failed' + }) + } +} + +/** Returns a fresh id_token suitable for game launch, refreshing if needed. */ +export async function loadFreshGameToken(env: Env): Promise { + const kcBase = getKeycloakBase(env) + const tokens = loadTokens(env) + if (!kcBase || !tokens) return null + + const refreshed = await refreshToken(kcBase, tokens.refresh_token) + if (!refreshed) { + clearStoredTokens(env) + return null + } + + const merged = mergeTokenSet(tokens, refreshed) + storeTokens(env, merged) + const launchToken = pickGameLaunchToken(merged) + if (!launchToken) { + console.error('[Auth] Invalid game token:', describeGameTokenIssue(merged)) + } + return launchToken +} + +/** Refreshes tokens and returns the user profile, or null when session is invalid. */ +export async function loadUserProfile(env: Env): Promise { + const kcBase = getKeycloakBase(env) + const tokens = loadTokens(env) + if (!kcBase || !tokens) return null + + const refreshed = await refreshToken(kcBase, tokens.refresh_token) + if (!refreshed) { + clearStoredTokens(env) + return null + } + + const merged = mergeTokenSet(tokens, refreshed) + storeTokens(env, merged) + return extractUser(merged) +} + +/** Clears local tokens and opens Keycloak logout in the browser. */ +export function logout(env: Env): void { + const kcBase = getKeycloakBase(env) + const tokens = loadTokens(env) + clearStoredTokens(env) + + if (kcBase && tokens?.id_token) { + const params = new URLSearchParams({ + client_id: AUTH_CLIENT_ID, + post_logout_redirect_uri: AUTH_REDIRECT_URI, + id_token_hint: tokens.id_token + }) + void shell.openExternal( + `${kcBase}/realms/${AUTH_REALM}/protocol/openid-connect/logout?${params}` + ) + } +} diff --git a/src/main/services/auth/handlers.ts b/src/main/services/auth/handlers.ts new file mode 100644 index 0000000..5573262 --- /dev/null +++ b/src/main/services/auth/handlers.ts @@ -0,0 +1,30 @@ +import { ipcMain, type BrowserWindow } from 'electron' +import type { Env } from '@shared/types/env' +import type { UserInfo } from '@shared/types/auth' +import { loadUserProfile, logout, setAuthEventSender, startLogin } from './flow' + +/** Registers IPC handlers for authentication (login, logout, load user). */ +export function registerAuthHandlers(win: BrowserWindow): void { + setAuthEventSender(win.webContents) + + ipcMain.handle('auth:login', async (_event, env: Env) => { + try { + await startLogin(env) + } catch (err) { + console.error('[Auth] Failed to open browser:', err) + win.webContents.send('auth:state-changed', { + env, + status: 'error', + error: 'browser_open_failed' + }) + } + }) + + ipcMain.handle('auth:logout', (_event, env: Env) => { + logout(env) + }) + + ipcMain.handle('auth:load-user', async (_event, env: Env): Promise => { + return loadUserProfile(env) + }) +} diff --git a/src/main/services/auth/index.ts b/src/main/services/auth/index.ts new file mode 100644 index 0000000..347962a --- /dev/null +++ b/src/main/services/auth/index.ts @@ -0,0 +1,3 @@ +export type { UserInfo } from '@shared/types/auth' +export { registerAuthHandlers } from './handlers' +export { handleOAuthCallback, loadFreshGameToken } from './flow' diff --git a/src/main/services/auth/jwt.ts b/src/main/services/auth/jwt.ts new file mode 100644 index 0000000..0ba957a --- /dev/null +++ b/src/main/services/auth/jwt.ts @@ -0,0 +1,88 @@ +import type { UserInfo } from '@shared/types/auth' +import type { TokenSet } from './types' + +const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i + +/** Decodes the payload segment of a JWT without verifying the signature. */ +export function decodeJwtPayload(token: string): Record { + const parts = token.split('.') + if (parts.length !== 3) throw new Error('Invalid JWT') + return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8')) +} + +/** Returns the JWT `sub` claim or null. */ +export function getJwtSub(token: string): string | null { + try { + const sub = decodeJwtPayload(token)['sub'] + return typeof sub === 'string' && sub.length > 0 ? sub : null + } catch { + return null + } +} + +/** Returns true when `sub` is a valid player UUID. */ +export function isValidPlayerSub(sub: string): boolean { + return UUID_RE.test(sub) +} + +/** Returns true when the JWT is not expired. */ +export function isJwtNotExpired(token: string): boolean { + try { + const exp = decodeJwtPayload(token)['exp'] + if (typeof exp !== 'number') return false + return exp > Math.floor(Date.now() / 1000) + } catch { + return false + } +} + +/** Returns the JWT `preferred_username` claim or null. */ +export function getJwtPreferredUsername(token: string): string | null { + try { + const username = decodeJwtPayload(token)['preferred_username'] + return typeof username === 'string' && username.length > 0 ? username : null + } catch { + return null + } +} + +/** Returns true when the token satisfies game-launch requirements. */ +export function isValidGameLaunchToken(token: string): boolean { + const sub = getJwtSub(token) + return ( + sub !== null && + isValidPlayerSub(sub) && + getJwtPreferredUsername(token) !== null && + isJwtNotExpired(token) + ) +} + +/** Picks the id_token used to launch the game client. */ +export function pickGameLaunchToken(tokens: TokenSet): string | null { + const token = tokens.id_token + if (!token || !isValidGameLaunchToken(token)) return null + return token +} + +/** Maps token claims to the renderer user profile. */ +export function extractUser(tokens: TokenSet): UserInfo { + const p = decodeJwtPayload(tokens.id_token) + return { + sub: p['sub'] as string, + username: ((p['preferred_username'] ?? p['name']) as string | undefined) ?? 'Unknown', + email: (p['email'] as string | undefined) ?? '' + } +} + +/** Human-readable reason when a game token is rejected. */ +export function describeGameTokenIssue(tokens: TokenSet): string { + const token = tokens.id_token + if (!token) return 'id_token missing — sign in again' + if (!isJwtNotExpired(token)) return 'id_token expired — sign in again' + const sub = getJwtSub(token) + if (!sub || !isValidPlayerSub(sub)) return `invalid sub UUID (${sub ?? 'empty'})` + if (!getJwtPreferredUsername(token)) { + return 'preferred_username missing from id_token (check Keycloak / profile scope)' + } + return 'unknown reason' +} diff --git a/src/main/services/auth/keycloakClient.ts b/src/main/services/auth/keycloakClient.ts new file mode 100644 index 0000000..350ae37 --- /dev/null +++ b/src/main/services/auth/keycloakClient.ts @@ -0,0 +1,58 @@ +import { getApiBase, ENDPOINTS } from '../../config/api' +import { AUTH_CLIENT_ID, AUTH_REALM, AUTH_REDIRECT_URI, AUTH_SCOPES } from '../../config/auth' +import type { Env } from '@shared/types/env' +import type { TokenSet } from './types' +import { isValidGameLaunchToken } from './jwt' + +/** Resolves the Keycloak base URL for an environment, or null if API base is unset. */ +export function getKeycloakBase(env: Env): string | null { + const base = getApiBase(env) + if (!base) return null + return ENDPOINTS.authBase(base) +} + +/** Exchanges an authorization code for tokens (PKCE). */ +export async function exchangeCode( + kcBase: string, + code: string, + verifier: string +): Promise { + const res = await fetch(`${kcBase}/realms/${AUTH_REALM}/protocol/openid-connect/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + client_id: AUTH_CLIENT_ID, + code, + redirect_uri: AUTH_REDIRECT_URI, + code_verifier: verifier + }).toString() + }) + if (!res.ok) throw new Error(`Token exchange failed (${res.status}): ${await res.text()}`) + return res.json() as Promise +} + +/** Refreshes tokens using a refresh_token grant. */ +export async function refreshToken(kcBase: string, token: string): Promise { + const res = await fetch(`${kcBase}/realms/${AUTH_REALM}/protocol/openid-connect/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + client_id: AUTH_CLIENT_ID, + refresh_token: token, + scope: AUTH_SCOPES + }).toString() + }) + if (!res.ok) return null + return res.json() as Promise +} + +/** Merges refreshed tokens while preserving a valid id_token for game launch. */ +export function mergeTokenSet(previous: TokenSet, refreshed: TokenSet): TokenSet { + const idCandidates = [refreshed.id_token, previous.id_token].filter(Boolean) + const id_token = + idCandidates.find((t) => isValidGameLaunchToken(t)) ?? refreshed.id_token ?? previous.id_token + + return { ...previous, ...refreshed, id_token } +} diff --git a/src/main/services/auth/pkce.ts b/src/main/services/auth/pkce.ts new file mode 100644 index 0000000..102c844 --- /dev/null +++ b/src/main/services/auth/pkce.ts @@ -0,0 +1,13 @@ +import crypto from 'crypto' + +/** Generates a PKCE code verifier and S256 challenge pair. */ +export function generatePKCE(): { verifier: string; challenge: string } { + const verifier = crypto.randomBytes(32).toString('base64url') + const challenge = crypto.createHash('sha256').update(verifier).digest('base64url') + return { verifier, challenge } +} + +/** Generates a random OAuth state parameter. */ +export function generateState(): string { + return crypto.randomBytes(16).toString('base64url') +} diff --git a/src/main/services/auth/tokenStorage.ts b/src/main/services/auth/tokenStorage.ts new file mode 100644 index 0000000..a19c68f --- /dev/null +++ b/src/main/services/auth/tokenStorage.ts @@ -0,0 +1,43 @@ +import { app, safeStorage } from 'electron' +import fs from 'fs' +import path from 'path' +import type { Env } from '@shared/types/env' +import type { TokenSet } from './types' + +/** Returns the encrypted token file path for the given environment. */ +function getTokenPath(env: Env): string { + const filename = env === 'universe' ? 'tokens.enc' : `tokens-${env}.enc` + return path.join(app.getPath('userData'), filename) +} + +/** Encrypts and writes the token set to disk. */ +export function storeTokens(env: Env, tokens: TokenSet): void { + const json = JSON.stringify(tokens) + if (safeStorage.isEncryptionAvailable()) { + fs.writeFileSync(getTokenPath(env), safeStorage.encryptString(json)) + } else { + console.warn('[Auth] safeStorage unavailable, plain JSON fallback') + fs.writeFileSync(getTokenPath(env), json, 'utf-8') + } +} + +/** Loads and decrypts the token set, or null if missing / corrupt. */ +export function loadTokens(env: Env): TokenSet | null { + const p = getTokenPath(env) + if (!fs.existsSync(p)) return null + try { + const buf = fs.readFileSync(p) + if (safeStorage.isEncryptionAvailable()) { + return JSON.parse(safeStorage.decryptString(buf)) as TokenSet + } + return JSON.parse(buf.toString('utf-8')) as TokenSet + } catch { + return null + } +} + +/** Deletes stored tokens for the environment. */ +export function clearStoredTokens(env: Env): void { + const p = getTokenPath(env) + if (fs.existsSync(p)) fs.unlinkSync(p) +} diff --git a/src/main/services/auth/types.ts b/src/main/services/auth/types.ts new file mode 100644 index 0000000..d0560a0 --- /dev/null +++ b/src/main/services/auth/types.ts @@ -0,0 +1,17 @@ +import type { Env } from '@shared/types/env' + +/** Token set persisted after OAuth code exchange or refresh. */ +export interface TokenSet { + access_token: string + refresh_token: string + id_token: string + expires_in: number + token_type: string +} + +/** In-memory PKCE session while the user completes login in the browser. */ +export interface PKCESession { + env: Env + verifier: string + state: string +} diff --git a/src/main/services/download/extract.ts b/src/main/services/download/extract.ts new file mode 100644 index 0000000..40b6789 --- /dev/null +++ b/src/main/services/download/extract.ts @@ -0,0 +1,26 @@ +import extract from 'extract-zip' +import type { BrowserWindow } from 'electron' +import { sendProgress } from './progress' + +/** Extracts the ZIP into `destPath` and reports extraction progress. */ +export async function extractZip( + zipFilePath: string, + destPath: string, + win: BrowserWindow +): Promise { + sendProgress(win, 70, { key: 'extracting', current: 0, total: 0 }) + + await extract(zipFilePath, { + dir: destPath, + onEntry: (_entry, zipFile) => { + if (zipFile.entryCount > 0) { + const pct = 70 + (zipFile.entriesRead / zipFile.entryCount) * 28 + sendProgress(win, pct, { + key: 'extracting', + current: zipFile.entriesRead, + total: zipFile.entryCount + }) + } + } + }) +} diff --git a/src/main/services/download/index.ts b/src/main/services/download/index.ts new file mode 100644 index 0000000..b23108c --- /dev/null +++ b/src/main/services/download/index.ts @@ -0,0 +1,2 @@ +export { downloadAndInstall } from './install' +export { resolveInstalledVersion } from './manifest' diff --git a/src/main/services/download/install.ts b/src/main/services/download/install.ts new file mode 100644 index 0000000..37f9f9b --- /dev/null +++ b/src/main/services/download/install.ts @@ -0,0 +1,43 @@ +import * as fs from 'fs' +import * as path from 'path' +import type { BrowserWindow } from 'electron' +import { GAME_INSTALL_SUBDIR } from '../../config/constants' +import type { Env } from '@shared/types/env' +import type { InstallResult } from '@shared/types/install' +import { downloadZip } from './zipFetch' +import { extractZip } from './extract' +import { resolveInstalledVersion } from './manifest' +import { sendProgress } from './progress' + +/** Downloads the game ZIP, extracts it, and returns the installed version info. */ +export async function downloadAndInstall( + env: Env, + installPath: string, + win: BrowserWindow +): Promise { + if (!fs.existsSync(installPath)) { + fs.mkdirSync(installPath, { recursive: true }) + } + + let zipFilePath: string | null = null + try { + zipFilePath = await downloadZip(env, installPath, win) + + const gameRoot = path.join(installPath, GAME_INSTALL_SUBDIR) + sendProgress(win, 68, { key: 'preparing' }) + if (fs.existsSync(gameRoot)) { + fs.rmSync(gameRoot, { recursive: true, force: true }) + } + fs.mkdirSync(gameRoot, { recursive: true }) + + await extractZip(zipFilePath, gameRoot, win) + sendProgress(win, 99, { key: 'cleaning' }) + fs.unlinkSync(zipFilePath) + const manifest = await resolveInstalledVersion(env, gameRoot) + sendProgress(win, 100, { key: 'completeInstall', version: manifest.version }) + return manifest + } catch (err) { + if (zipFilePath && fs.existsSync(zipFilePath)) fs.unlinkSync(zipFilePath) + throw err + } +} diff --git a/src/main/services/download/manifest.ts b/src/main/services/download/manifest.ts new file mode 100644 index 0000000..08eb4bc --- /dev/null +++ b/src/main/services/download/manifest.ts @@ -0,0 +1,67 @@ +import * as fs from 'fs' +import * as path from 'path' +import { fetchRemoteGameVersion } from '../version' +import type { Env } from '@shared/types/env' +import type { InstallResult } from '@shared/types/install' + +/** Locates `version.json` at the game root or one subdirectory deep. */ +function findVersionManifestPath(gameRoot: string): string | null { + const direct = path.join(gameRoot, 'version.json') + try { + if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct + } catch { + /* try nested */ + } + + try { + for (const entry of fs.readdirSync(gameRoot, { withFileTypes: true })) { + if (!entry.isDirectory()) continue + const nested = path.join(gameRoot, entry.name, 'version.json') + try { + if (fs.existsSync(nested) && fs.statSync(nested).isFile()) return nested + } catch { + /* continue */ + } + } + } catch { + return null + } + + return null +} + +/** Reads version metadata from the local `version.json` manifest. */ +function readVersionManifest(gameRoot: string): InstallResult { + const manifestPath = findVersionManifestPath(gameRoot) + if (!manifestPath) { + console.warn('[Download] version.json not found under', gameRoot) + return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } + } + try { + const json = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as { + version?: string + releaseDate?: string + } + return { + version: json.version ?? 'unknown', + releaseDate: json.releaseDate ?? new Date().toISOString().split('T')[0] + } + } catch { + return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } + } +} + +/** + * Returns installed version aligned with remote `/version` when available. + */ +export async function resolveInstalledVersion(env: Env, gameRoot: string): Promise { + const fromFile = readVersionManifest(gameRoot) + const fromApi = await fetchRemoteGameVersion(env) + if (fromApi?.version) { + return { + version: fromApi.version, + releaseDate: fromApi.releaseDate ?? fromFile.releaseDate + } + } + return fromFile +} diff --git a/src/main/services/download/progress.ts b/src/main/services/download/progress.ts new file mode 100644 index 0000000..c181961 --- /dev/null +++ b/src/main/services/download/progress.ts @@ -0,0 +1,12 @@ +import type { BrowserWindow } from 'electron' +import type { InstallProgressLabel } from '@shared/types/installProgress' + +/** Sends install/download progress to the renderer (0–100 and structured label key). */ +export function sendProgress( + win: BrowserWindow, + progress: number, + label: InstallProgressLabel +): void { + if (win.isDestroyed()) return + win.webContents.send('files:progress', Math.min(100, Math.round(progress)), label) +} diff --git a/src/main/services/download/zipFetch.ts b/src/main/services/download/zipFetch.ts new file mode 100644 index 0000000..45c19d0 --- /dev/null +++ b/src/main/services/download/zipFetch.ts @@ -0,0 +1,115 @@ +import axios from 'axios' +import * as fs from 'fs' +import * as path from 'path' +import type { BrowserWindow } from 'electron' +import { + DOWNLOAD_PROGRESS, + DOWNLOAD_USER_AGENT, + GAME_DOWNLOAD_ZIP_NAME, + HTTP_TIMEOUT_MS +} from '../../config/constants' +import type { Env } from '@shared/types/env' +import { getZipUrl } from './zipUrl' +import { sendProgress } from './progress' + +const ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]) + +/** Verifies the downloaded file starts with a ZIP local file header. */ +export function assertIsZipFile(filePath: string, sourceUrl: string): void { + let fd: number + try { + fd = fs.openSync(filePath, 'r') + } catch { + throw new Error(`Cannot read downloaded file — ${sourceUrl}`) + } + try { + const head = Buffer.alloc(4) + const read = fs.readSync(fd, head, 0, 4, 0) + if (read < 4 || !head.subarray(0, 4).equals(ZIP_MAGIC)) { + throw new Error( + `Received file is not a ZIP archive (often an HTML error page, e.g. HTTP 404). Check URL: ${sourceUrl}` + ) + } + } finally { + try { + fs.closeSync(fd) + } catch { + /* ignore */ + } + } +} + +/** Streams the game ZIP to disk and reports progress to the renderer. */ +export async function downloadZip(env: Env, destPath: string, win: BrowserWindow): Promise { + sendProgress(win, 0, { key: 'connecting' }) + + const url = getZipUrl(env) + + let response: Awaited>> + try { + response = await axios.get(url, { + responseType: 'stream', + timeout: HTTP_TIMEOUT_MS.download, + headers: { + 'User-Agent': DOWNLOAD_USER_AGENT, + Accept: 'application/zip, application/octet-stream, */*' + }, + validateStatus: (s) => s >= 200 && s < 300 + }) + } catch (err) { + if (axios.isAxiosError(err)) { + const code = err.response?.status + const hint = + code === 404 + ? ' — archive not found (404). Set VITE_GAME_DOWNLOAD_BASE_* if ZIPs are hosted elsewhere.' + : '' + throw new Error(`Download failed${code ? ` (HTTP ${code})` : ''}${hint}\n${url}`) + } + throw err + } + + if (response.status !== 200) { + throw new Error(`Download rejected (HTTP ${response.status})\n${url}`) + } + + const totalLength = Number(response.headers['content-length'] ?? 0) + let downloaded = 0 + let lastProgressReport = 0 + + const zipFilePath = path.join(destPath, GAME_DOWNLOAD_ZIP_NAME) + const writer = fs.createWriteStream(zipFilePath) + + await new Promise((resolve, reject) => { + const stream = response.data + stream.on('error', reject) + writer.on('error', reject) + writer.on('finish', () => resolve()) + + stream.on('data', (chunk: Buffer) => { + downloaded += chunk.length + if (!writer.write(chunk)) { + stream.pause() + writer.once('drain', () => stream.resume()) + } + if (totalLength > 0) { + sendProgress(win, (downloaded / totalLength) * DOWNLOAD_PROGRESS.zipWeight, { + key: 'downloading', + downloaded, + total: totalLength + }) + } else if (downloaded - lastProgressReport >= DOWNLOAD_PROGRESS.unknownChunkBytes) { + lastProgressReport = downloaded + sendProgress( + win, + Math.min(65, 15 + (downloaded / DOWNLOAD_PROGRESS.unknownSizeCapBytes) * 50), + { key: 'downloadingIndeterminate', downloaded } + ) + } + }) + + stream.on('end', () => writer.end()) + }) + + assertIsZipFile(zipFilePath, url) + return zipFilePath +} diff --git a/src/main/services/download/zipUrl.ts b/src/main/services/download/zipUrl.ts new file mode 100644 index 0000000..da6242e --- /dev/null +++ b/src/main/services/download/zipUrl.ts @@ -0,0 +1,30 @@ +import { + ENDPOINTS, + getDownloadBase, + getTestingZipUrlForPlatform, + getUniverseZipUrlForPlatform +} from '../../config/api' +import type { Env } from '@shared/types/env' + +/** Resolves the ZIP download URL for the current platform and environment. */ +export function getZipUrl(env: Env): string { + const platform = process.platform + + if (env === 'universe') { + const direct = getUniverseZipUrlForPlatform(platform) + if (direct) return direct + } + + if (env === 'universe-testing') { + const direct = getTestingZipUrlForPlatform(platform) + if (direct) return direct + } + + const base = getDownloadBase(env) + if (!base) throw new Error(`Env "${env}" is not configured — missing base URL.`) + + if (platform === 'win32') return ENDPOINTS.zipWin32(base) + if (platform === 'linux') return ENDPOINTS.zipLinux(base) + if (platform === 'darwin') return ENDPOINTS.zipDarwin(base) + throw new Error(`Unsupported platform: ${platform}`) +} diff --git a/src/main/services/downloader.ts b/src/main/services/downloader.ts deleted file mode 100644 index f32eca5..0000000 --- a/src/main/services/downloader.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { BrowserWindow } from 'electron' -import axios from 'axios' -import * as fs from 'fs' -import * as path from 'path' -import extract from 'extract-zip' -import { - getDownloadBase, - getUniverseZipUrlForPlatform, - getTestingZipUrlForPlatform, - ENDPOINTS -} from '../config/api' -import { fetchRemoteGameVersion } from './version' -import type { Env } from '@shared/types/env' - -/** Sous-dossier d’installation du payload du jeu (ZIP extrait ici, pas à la racine du chemin choisi). */ -export const GAME_INSTALL_SUBDIR = 'DyingStar' - -// ─── Types ──────────────────────────────────────────────────────────────────── - -export interface InstallResult { - version: string - releaseDate: string -} - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -function getZipUrl(env: Env): string { - const platform = process.platform - - if (env === 'universe') { - const direct = getUniverseZipUrlForPlatform(platform) - if (direct) return direct - } - - if (env === 'universe-testing') { - const direct = getTestingZipUrlForPlatform(platform) - if (direct) return direct - } - - const base = getDownloadBase(env) - if (!base) throw new Error(`Env "${env}" non configuré — URL de base manquante.`) - - if (platform === 'win32') return ENDPOINTS.zipWin32(base) - if (platform === 'linux') return ENDPOINTS.zipLinux(base) - if (platform === 'darwin') return ENDPOINTS.zipDarwin(base) - throw new Error(`Plateforme non supportée : ${platform}`) -} - -const ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]) - -function assertIsZipFile(filePath: string, sourceUrl: string): void { - let fd: number - try { - fd = fs.openSync(filePath, 'r') - } catch { - throw new Error(`Impossible de lire le fichier téléchargé — ${sourceUrl}`) - } - try { - const head = Buffer.alloc(4) - const read = fs.readSync(fd, head, 0, 4, 0) - if (read < 4 || !head.subarray(0, 4).equals(ZIP_MAGIC)) { - throw new Error( - `Le fichier reçu n’est pas une archive ZIP (souvent une page d’erreur HTML, ex. HTTP 404). Vérifiez l’URL : ${sourceUrl}` - ) - } - } finally { - try { - fs.closeSync(fd) - } catch { - /* ignore */ - } - } -} - -function sendProgress(win: BrowserWindow, progress: number, label: string): void { - if (win.isDestroyed()) return - win.webContents.send('files:progress', Math.min(100, Math.round(progress)), label) -} - -// ─── Téléchargement ─────────────────────────────────────────────────────────── - -async function downloadZip(env: Env, destPath: string, win: BrowserWindow): Promise { - sendProgress(win, 0, 'Connexion au serveur...') - - const url = getZipUrl(env) - - let response: Awaited>> - try { - response = await axios.get(url, { - responseType: 'stream', - timeout: 600_000, - headers: { - 'User-Agent': 'DyingStar-Launcher/1.0', - Accept: 'application/zip, application/octet-stream, */*' - }, - validateStatus: (s) => s >= 200 && s < 300 - }) - } catch (err) { - if (axios.isAxiosError(err)) { - const code = err.response?.status - const hint = - code === 404 - ? ` — l’archive n’existe pas à cet emplacement (404). Définissez VITE_GAME_DOWNLOAD_BASE_* si les ZIP sont sur un autre hôte.` - : '' - throw new Error(`Téléchargement impossible${code ? ` (HTTP ${code})` : ''}${hint}\n${url}`) - } - throw err - } - - if (response.status !== 200) { - throw new Error(`Téléchargement refusé (HTTP ${response.status})\n${url}`) - } - - const totalLength = Number(response.headers['content-length'] ?? 0) - let downloaded = 0 - let lastProgressReport = 0 - - const zipFilePath = path.join(destPath, '__game_download.zip') - const writer = fs.createWriteStream(zipFilePath) - - await new Promise((resolve, reject) => { - const stream = response.data - stream.on('error', reject) - writer.on('error', reject) - writer.on('finish', () => resolve()) - - stream.on('data', (chunk: Buffer) => { - downloaded += chunk.length - if (!writer.write(chunk)) { - stream.pause() - writer.once('drain', () => stream.resume()) - } - if (totalLength > 0) { - sendProgress( - win, - (downloaded / totalLength) * 70, - `Téléchargement... (${formatBytes(downloaded)} / ${formatBytes(totalLength)})` - ) - } else if (downloaded - lastProgressReport >= 2 * 1024 * 1024) { - lastProgressReport = downloaded - sendProgress(win, Math.min(65, 15 + downloaded / (80 * 1024 * 1024) * 50), - `Téléchargement... (${formatBytes(downloaded)})`) - } - }) - - stream.on('end', () => writer.end()) - }) - - assertIsZipFile(zipFilePath, url) - - return zipFilePath -} - -// ─── Extraction ─────────────────────────────────────────────────────────────── - -async function extractZip(zipFilePath: string, destPath: string, win: BrowserWindow): Promise { - sendProgress(win, 70, 'Extraction des fichiers...') - - await extract(zipFilePath, { - dir: destPath, - onEntry: (_entry, zipFile) => { - if (zipFile.entryCount > 0) { - const pct = 70 + (zipFile.entriesRead / zipFile.entryCount) * 28 - sendProgress(win, pct, `Extraction... (${zipFile.entriesRead} / ${zipFile.entryCount} fichiers)`) - } - } - }) -} - -// ─── Lecture version.json ───────────────────────────────────────────────────── - -function findVersionManifestPath(gameRoot: string): string | null { - const direct = path.join(gameRoot, 'version.json') - try { - if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct - } catch { - /* continue */ - } - - try { - for (const entry of fs.readdirSync(gameRoot, { withFileTypes: true })) { - if (!entry.isDirectory()) continue - const nested = path.join(gameRoot, entry.name, 'version.json') - try { - if (fs.existsSync(nested) && fs.statSync(nested).isFile()) return nested - } catch { - /* continue */ - } - } - } catch { - return null - } - - return null -} - -function readVersionManifest(gameRoot: string): InstallResult { - const manifestPath = findVersionManifestPath(gameRoot) - if (!manifestPath) { - console.warn('[Downloader] version.json introuvable sous', gameRoot) - return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } - } - try { - const json = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as { - version?: string; releaseDate?: string - } - return { - version: json.version ?? 'unknown', - releaseDate: json.releaseDate ?? new Date().toISOString().split('T')[0] - } - } catch { - return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } - } -} - -/** Version locale alignée sur l’API /version (référence du bandeau « mise à jour »). */ -export async function resolveInstalledVersion(env: Env, gameRoot: string): Promise { - const fromFile = readVersionManifest(gameRoot) - const fromApi = await fetchRemoteGameVersion(env) - if (fromApi?.version) { - return { - version: fromApi.version, - releaseDate: fromApi.releaseDate ?? fromFile.releaseDate - } - } - return fromFile -} - -// ─── Point d'entrée ─────────────────────────────────────────────────────────── - -export async function downloadAndInstall( - env: Env, - installPath: string, - win: BrowserWindow -): Promise { - if (!fs.existsSync(installPath)) { - fs.mkdirSync(installPath, { recursive: true }) - } - - let zipFilePath: string | null = null - try { - zipFilePath = await downloadZip(env, installPath, win) - - const gameRoot = path.join(installPath, GAME_INSTALL_SUBDIR) - sendProgress(win, 68, 'Préparation du dossier du jeu...') - if (fs.existsSync(gameRoot)) { - fs.rmSync(gameRoot, { recursive: true, force: true }) - } - fs.mkdirSync(gameRoot, { recursive: true }) - - await extractZip(zipFilePath, gameRoot, win) - sendProgress(win, 99, 'Nettoyage...') - fs.unlinkSync(zipFilePath) - const manifest = await resolveInstalledVersion(env, gameRoot) - sendProgress(win, 100, `Installation terminée — v${manifest.version}`) - return manifest - } catch (err) { - if (zipFilePath && fs.existsSync(zipFilePath)) fs.unlinkSync(zipFilePath) - throw err - } -} - -// ─── Utilitaires ────────────────────────────────────────────────────────────── - -function formatBytes(bytes: number): string { - if (bytes < 1024) return `${bytes} o` - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} Ko` - return `${(bytes / (1024 * 1024)).toFixed(1)} Mo` -} \ No newline at end of file diff --git a/src/main/services/game.ts b/src/main/services/game.ts deleted file mode 100644 index 2619c4f..0000000 --- a/src/main/services/game.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { ipcMain, dialog, BrowserWindow } from 'electron' -import { spawn, type ChildProcess } from 'child_process' -import * as path from 'path' -import * as fs from 'fs' -import { downloadAndInstall, GAME_INSTALL_SUBDIR, resolveInstalledVersion } from './downloader' -import { loadFreshGameToken } from './auth' -import { clearDyingStarGodotCaches } from './godotUserdataCache' -import type { Env } from '@shared/types/env' - -// ─── Types ──────────────────────────────────────────────────────────────────── - -interface GameState { - installedVersion: string | null - availableVersion: string | null - releaseDate: string | null - status: 'not-installed' | 'up-to-date' | 'update-available' | 'checking' -} - -// ─── Exécutables par OS ─────────────────────────────────────────────────────── - -const GAME_EXECUTABLES: Partial> = { - win32: 'DyingStar.exe', - linux: 'DyingStar.x86_64', - darwin: 'DyingStar.app' -} - -function getGameRoot(installPath: string): string { - return path.join(installPath, GAME_INSTALL_SUBDIR) -} - -function getExecutablePath(installPath: string): string { - const exe = GAME_EXECUTABLES[process.platform] - if (!exe) throw new Error(`Plateforme non supportée : ${process.platform}`) - const inPayload = path.join(getGameRoot(installPath), exe) - if (fs.existsSync(inPayload)) return inPayload - const legacy = path.join(installPath, exe) - if (fs.existsSync(legacy)) return legacy - return inPayload -} - -/** Cherche CHANGELOG.md à la racine de `root` ou dans un seul niveau de sous-dossier (ZIP avec dossier racine). */ -function findChangelogUnderRoot(root: string): string | null { - const direct = path.join(root, 'CHANGELOG.md') - try { - if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct - } catch { - /* continue */ - } - try { - const entries = fs.readdirSync(root, { withFileTypes: true }) - for (const e of entries) { - if (!e.isDirectory()) continue - const nested = path.join(root, e.name, 'CHANGELOG.md') - try { - if (fs.existsSync(nested) && fs.statSync(nested).isFile()) return nested - } catch { - /* continue */ - } - } - } catch { - return null - } - return null -} - -/** - * CHANGELOG : si le jeu est sous `…/DyingStar/`, on ne lit que là (pas un ancien fichier à la racine install). - * Sinon installation legacy → racine `installPath`. - */ -function findChangelogPath(installPath: string): string | null { - const resolved = path.resolve(installPath) - const gameRoot = getGameRoot(resolved) - try { - if (fs.existsSync(gameRoot) && fs.statSync(gameRoot).isDirectory()) { - return findChangelogUnderRoot(gameRoot) - } - } catch { - /* fallback legacy */ - } - return findChangelogUnderRoot(resolved) -} - -// ─── Suivi du processus jeu ─────────────────────────────────────────────────── - -let launcherWindow: BrowserWindow | null = null -let trackedGamePid: number | null = null -let gamePollTimer: ReturnType | null = null - -function isPidAlive(pid: number): boolean { - try { - process.kill(pid, 0) - return true - } catch { - return false - } -} - -function notifyGameRunning(running: boolean): void { - if (!launcherWindow || launcherWindow.isDestroyed()) return - launcherWindow.webContents.send('game:running-changed', { running }) -} - -function clearGameWatch(): void { - if (gamePollTimer) { - clearInterval(gamePollTimer) - gamePollTimer = null - } - trackedGamePid = null -} - -function stopGameWatchIfExited(): void { - if (trackedGamePid === null) return - if (!isPidAlive(trackedGamePid)) { - clearGameWatch() - notifyGameRunning(false) - } -} - -function watchGameProcess(child: ChildProcess): void { - clearGameWatch() - const pid = child.pid - if (!pid) return - - trackedGamePid = pid - notifyGameRunning(true) - - const onDone = (): void => { - if (trackedGamePid !== pid) return - clearGameWatch() - notifyGameRunning(false) - } - - child.once('exit', onDone) - child.once('error', onDone) - - gamePollTimer = setInterval(stopGameWatchIfExited, 2000) -} - -function isGameRunning(): boolean { - return trackedGamePid !== null && isPidAlive(trackedGamePid) -} - -// ─── Handlers IPC ───────────────────────────────────────────────────────────── - -export function registerFilesHandlers(win: BrowserWindow): void { - launcherWindow = win - - // ── Sélection du répertoire d'installation ───────────────────────────────── - ipcMain.removeHandler('files:select-directory') - ipcMain.handle('files:select-directory', async () => { - const result = await dialog.showOpenDialog(win, { - title: "Répertoire d'installation", - buttonLabel: 'Choisir ce dossier', - properties: ['openDirectory', 'createDirectory'] - }) - if (result.canceled || result.filePaths.length === 0) return null - return result.filePaths[0] - }) - - // ── Téléchargement + installation → retourne { version, releaseDate } ────── - ipcMain.removeHandler('files:install') - ipcMain.handle('files:install', async (_event, env: Env, installPath: string) => { - if (!installPath || typeof installPath !== 'string') { - throw new Error("Chemin d'installation invalide.") - } - // Le résultat InstallResult est sérialisé et renvoyé au renderer - return await downloadAndInstall(env, installPath, win) - }) - - // ── Lancement du jeu ─────────────────────────────────────────────────────── - ipcMain.removeHandler('game:launch') - ipcMain.removeHandler('game:is-running') - ipcMain.removeHandler('files:clear-godot-cache') - ipcMain.handle('files:clear-godot-cache', async () => clearDyingStarGodotCaches()) - - ipcMain.removeHandler('files:read-changelog') - ipcMain.removeHandler('files:resolve-installed-version') - ipcMain.handle('files:read-changelog', async (_event, installPath: string): Promise => { - if (!installPath || typeof installPath !== 'string') return null - const root = path.resolve(installPath) - const changelogFile = findChangelogPath(root) - if (!changelogFile) return null - try { - return fs.readFileSync(changelogFile, 'utf-8') - } catch { - return null - } - }) - - ipcMain.handle( - 'files:resolve-installed-version', - async (_event, env: Env, installPath: string): Promise<{ version: string; releaseDate: string } | null> => { - if (!installPath || typeof installPath !== 'string') return null - const gameRoot = getGameRoot(path.resolve(installPath)) - if (!fs.existsSync(gameRoot)) return null - return resolveInstalledVersion(env, gameRoot) - } - ) - - ipcMain.handle('game:launch', async (_event, env: Env, installPath: string) => { - if (isGameRunning()) { - throw new Error('Le jeu est déjà en cours d\'exécution.') - } - - const token = await loadFreshGameToken(env) - if (!token) { - throw new Error( - 'Session invalide ou jeton incomplet (identifiant ou pseudo manquant). Reconnectez-vous puis réessayez.' - ) - } - - const exePath = getExecutablePath(installPath) - if (!fs.existsSync(exePath)) { - throw new Error(`Exécutable introuvable : ${exePath}`) - } - if (process.platform === 'linux') fs.chmodSync(exePath, 0o755) - - const gameRoot = getGameRoot(installPath) - const cwd = fs.existsSync(gameRoot) ? gameRoot : installPath - - // Le client Godot ne lit que `--token=` (un seul argument avec '='). - // Voir DyingStar/server/client.gd (branche develop). - const child = spawn(exePath, [`--token=${token}`], { - detached: true, - stdio: 'ignore', - cwd - }) - watchGameProcess(child) - child.unref() - }) - - ipcMain.handle('game:is-running', () => isGameRunning()) - - // ── État du jeu ──────────────────────────────────────────────────────────── - ipcMain.removeHandler('game:get-state') - ipcMain.handle('game:get-state', async (_event, _env: Env): Promise => { - // TODO: lire version locale + interroger serveur - return { - installedVersion: null, - availableVersion: '1.0.0', - releaseDate: '2026-01-01', - status: 'not-installed' - } - }) -} \ No newline at end of file diff --git a/src/main/services/game/handlers.ts b/src/main/services/game/handlers.ts new file mode 100644 index 0000000..5082e1b --- /dev/null +++ b/src/main/services/game/handlers.ts @@ -0,0 +1,78 @@ +import { dialog, ipcMain, type BrowserWindow } from 'electron' +import * as fs from 'fs' +import * as path from 'path' +import { downloadAndInstall } from '../download' +import { resolveInstalledVersion } from '../download/manifest' +import { clearDyingStarGodotCaches } from '../godotUserdataCache' +import type { Env } from '@shared/types/env' +import { findChangelogPath, getGameRoot } from './paths' +import { launchGame } from './launch' +import { isGameRunning, setLauncherWindow } from './processWatch' +import { getInstallDialogStrings } from '../../l10n/dialogs' + +/** Registers IPC handlers for install paths, changelog, cache, and game launch. */ +export function registerFilesHandlers(win: BrowserWindow): void { + setLauncherWindow(win) + + ipcMain.removeHandler('files:select-directory') + ipcMain.handle('files:select-directory', async () => { + const dialogStrings = getInstallDialogStrings() + const result = await dialog.showOpenDialog(win, { + title: dialogStrings.selectDirectoryTitle, + buttonLabel: dialogStrings.selectDirectoryButton, + properties: ['openDirectory', 'createDirectory'] + }) + if (result.canceled || result.filePaths.length === 0) return null + return result.filePaths[0] + }) + + ipcMain.removeHandler('files:install') + ipcMain.handle('files:install', async (_event, env: Env, installPath: string) => { + if (!installPath || typeof installPath !== 'string') { + throw new Error('Invalid installation path.') + } + return downloadAndInstall(env, installPath, win) + }) + + ipcMain.removeHandler('game:launch') + ipcMain.removeHandler('game:is-running') + ipcMain.removeHandler('files:clear-godot-cache') + ipcMain.handle('files:clear-godot-cache', async () => clearDyingStarGodotCaches()) + + ipcMain.removeHandler('files:read-changelog') + ipcMain.removeHandler('files:resolve-installed-version') + + ipcMain.handle( + 'files:read-changelog', + async (_event, installPath: string): Promise => { + if (!installPath || typeof installPath !== 'string') return null + const changelogFile = findChangelogPath(path.resolve(installPath)) + if (!changelogFile) return null + try { + return fs.readFileSync(changelogFile, 'utf-8') + } catch { + return null + } + } + ) + + ipcMain.handle( + 'files:resolve-installed-version', + async ( + _event, + env: Env, + installPath: string + ): Promise<{ version: string; releaseDate: string } | null> => { + if (!installPath || typeof installPath !== 'string') return null + const gameRoot = getGameRoot(path.resolve(installPath)) + if (!fs.existsSync(gameRoot)) return null + return resolveInstalledVersion(env, gameRoot) + } + ) + + ipcMain.handle('game:launch', async (_event, env: Env, installPath: string) => { + await launchGame(installPath, env) + }) + + ipcMain.handle('game:is-running', () => isGameRunning()) +} diff --git a/src/main/services/game/index.ts b/src/main/services/game/index.ts new file mode 100644 index 0000000..ecfb20c --- /dev/null +++ b/src/main/services/game/index.ts @@ -0,0 +1,2 @@ +export { registerFilesHandlers } from './handlers' +export { GAME_INSTALL_SUBDIR } from './paths' diff --git a/src/main/services/game/launch.ts b/src/main/services/game/launch.ts new file mode 100644 index 0000000..0d3fe99 --- /dev/null +++ b/src/main/services/game/launch.ts @@ -0,0 +1,39 @@ +import { spawn } from 'child_process' +import * as fs from 'fs' +import { loadFreshGameToken } from '../auth' +import { getExecutablePath, getGameRoot } from './paths' +import { isGameRunning, watchGameProcess } from './processWatch' + +/** Spawns the game executable with a fresh JWT (`--token=`). */ +export async function launchGame( + installPath: string, + env: Parameters[0] +): Promise { + if (isGameRunning()) { + throw new Error('Game is already running.') + } + + const token = await loadFreshGameToken(env) + if (!token) { + throw new Error( + 'Invalid session or incomplete token (missing id or username). Sign in again and retry.' + ) + } + + const exePath = getExecutablePath(installPath) + if (!fs.existsSync(exePath)) { + throw new Error(`Executable not found: ${exePath}`) + } + if (process.platform === 'linux') fs.chmodSync(exePath, 0o755) + + const gameRoot = getGameRoot(installPath) + const cwd = fs.existsSync(gameRoot) ? gameRoot : installPath + + const child = spawn(exePath, [`--token=${token}`], { + detached: true, + stdio: 'ignore', + cwd + }) + watchGameProcess(child) + child.unref() +} diff --git a/src/main/services/game/paths.ts b/src/main/services/game/paths.ts new file mode 100644 index 0000000..f00cc6d --- /dev/null +++ b/src/main/services/game/paths.ts @@ -0,0 +1,61 @@ +import * as fs from 'fs' +import * as path from 'path' +import { GAME_EXECUTABLES, GAME_INSTALL_SUBDIR } from '../../config/constants' + +export { GAME_INSTALL_SUBDIR } + +/** Returns the game payload directory inside the user install path. */ +export function getGameRoot(installPath: string): string { + return path.join(installPath, GAME_INSTALL_SUBDIR) +} + +/** Resolves the platform game executable (payload dir first, then legacy install root). */ +export function getExecutablePath(installPath: string): string { + const exe = GAME_EXECUTABLES[process.platform] + if (!exe) throw new Error(`Unsupported platform: ${process.platform}`) + const inPayload = path.join(getGameRoot(installPath), exe) + if (fs.existsSync(inPayload)) return inPayload + const legacy = path.join(installPath, exe) + if (fs.existsSync(legacy)) return legacy + return inPayload +} + +/** Finds CHANGELOG.md at the root or one level below `root`. */ +function findChangelogUnderRoot(root: string): string | null { + const direct = path.join(root, 'CHANGELOG.md') + try { + if (fs.existsSync(direct) && fs.statSync(direct).isFile()) return direct + } catch { + /* try subdirectories */ + } + try { + for (const e of fs.readdirSync(root, { withFileTypes: true })) { + if (!e.isDirectory()) continue + const nested = path.join(root, e.name, 'CHANGELOG.md') + try { + if (fs.existsSync(nested) && fs.statSync(nested).isFile()) return nested + } catch { + /* continue */ + } + } + } catch { + return null + } + return null +} + +/** + * Resolves CHANGELOG path: prefers `installPath/DyingStar/`, falls back to legacy layout. + */ +export function findChangelogPath(installPath: string): string | null { + const resolved = path.resolve(installPath) + const gameRoot = getGameRoot(resolved) + try { + if (fs.existsSync(gameRoot) && fs.statSync(gameRoot).isDirectory()) { + return findChangelogUnderRoot(gameRoot) + } + } catch { + /* legacy layout */ + } + return findChangelogUnderRoot(resolved) +} diff --git a/src/main/services/game/processWatch.ts b/src/main/services/game/processWatch.ts new file mode 100644 index 0000000..502e209 --- /dev/null +++ b/src/main/services/game/processWatch.ts @@ -0,0 +1,71 @@ +import type { BrowserWindow } from 'electron' +import type { ChildProcess } from 'child_process' +import { GAME_PROCESS_POLL_MS } from '../../config/constants' + +let launcherWindow: BrowserWindow | null = null +let trackedGamePid: number | null = null +let gamePollTimer: ReturnType | null = null + +/** Binds the window that receives `game:running-changed` events. */ +export function setLauncherWindow(win: BrowserWindow): void { + launcherWindow = win +} + +/** Returns true when a game process launched by the launcher is still alive. */ +export function isGameRunning(): boolean { + return trackedGamePid !== null && isPidAlive(trackedGamePid) +} + +/** Sends running state to the renderer. */ +function notifyGameRunning(running: boolean): void { + if (!launcherWindow || launcherWindow.isDestroyed()) return + launcherWindow.webContents.send('game:running-changed', { running }) +} + +/** Returns true if sending signal 0 to the pid does not throw (process exists). */ +function isPidAlive(pid: number): boolean { + try { + process.kill(pid, 0) + return true + } catch { + return false + } +} + +/** Stops polling and clears the tracked game PID. */ +function clearGameWatch(): void { + if (gamePollTimer) { + clearInterval(gamePollTimer) + gamePollTimer = null + } + trackedGamePid = null +} + +/** Poll callback: clears watch and notifies renderer when the game process exited. */ +function stopGameWatchIfExited(): void { + if (trackedGamePid === null) return + if (!isPidAlive(trackedGamePid)) { + clearGameWatch() + notifyGameRunning(false) + } +} + +/** Tracks a detached game child process until it exits. */ +export function watchGameProcess(child: ChildProcess): void { + clearGameWatch() + const pid = child.pid + if (!pid) return + + trackedGamePid = pid + notifyGameRunning(true) + + const onDone = (): void => { + if (trackedGamePid !== pid) return + clearGameWatch() + notifyGameRunning(false) + } + + child.once('exit', onDone) + child.once('error', onDone) + gamePollTimer = setInterval(stopGameWatchIfExited, GAME_PROCESS_POLL_MS) +} diff --git a/src/main/services/gameStatus.ts b/src/main/services/gameStatus.ts index fab2d94..2a8e5ba 100644 --- a/src/main/services/gameStatus.ts +++ b/src/main/services/gameStatus.ts @@ -1,13 +1,11 @@ import { ipcMain } from 'electron' import { getApiBase, ENDPOINTS, getStatusComponentId, getStatusMetricId } from '../config/api' +import { HTTP_TIMEOUT_MS } from '../config/constants' import type { Env } from '@shared/types/env' import type { GameStatusResult, ServerStatusValue } from '@shared/types/game' export type { GameStatusResult, ServerStatusValue } from '@shared/types/game' -// Mapping value → type interne -// 1: Operational, 2: Performance Issues, 3: Partial Outage, -// 4: Major Outage, 5: Unknown, 6: Under Maintenance const STATUS_MAP: Record = { 1: 'online', 2: 'degraded', @@ -17,15 +15,15 @@ const STATUS_MAP: Record = { 6: 'maintenance' } -// ─── Fetch helpers ──────────────────────────────────────────────────────────── - -async function fetchWithTimeout(url: string, timeoutMs = 8_000): Promise { +/** Performs a fetch with an abort timeout. */ +async function fetchWithTimeout(url: string, timeoutMs = HTTP_TIMEOUT_MS.api): Promise { return fetch(url, { signal: AbortSignal.timeout(timeoutMs), - headers: { 'Cache-Control': 'no-cache', 'Accept': 'application/json' } + headers: { 'Cache-Control': 'no-cache', Accept: 'application/json' } }) } +/** Parses Cachet component status JSON into a numeric value and label. */ function parseCachetComponentStatus(json: unknown): { value: number | undefined; label: string } { if (!json || typeof json !== 'object') return { value: undefined, label: 'invalid_json' } const root = json as Record @@ -42,6 +40,7 @@ function parseCachetComponentStatus(json: unknown): { value: number | undefined; } } +/** Fetches server operational status from the status API. */ async function fetchStatus( apiBase: string, componentId: number @@ -55,22 +54,23 @@ async function fetchStatus( const { value, label } = parseCachetComponentStatus(json) if (value === undefined) { - console.warn('[GameStatus] Statut composant illisible', { url, label }) + console.warn('[GameStatus] Unreadable component status', { url, label }) return { status: 'unknown', label: label || 'parse_error' } } const mapped = STATUS_MAP[value] if (!mapped) { - console.warn('[GameStatus] Valeur status inconnue', { url, value, label }) + console.warn('[GameStatus] Unknown status value', { url, value, label }) return { status: 'unknown', label } } return { status: mapped, label } } catch (err) { - console.warn('[GameStatus] fetch status échoué', err) + console.warn('[GameStatus] Status fetch failed', err) return { status: 'unknown', label: 'fetch_error' } } } +/** Parses connected player count from Cachet metrics API response. */ function parseMetricPoints(json: unknown): number { if (!json || typeof json !== 'object') return 0 const root = json as { data?: unknown } @@ -83,6 +83,7 @@ function parseMetricPoints(json: unknown): number { return Number.isFinite(n) ? n : 0 } +/** Fetches the current player count metric. */ async function fetchPlayers(apiBase: string, metricId: number): Promise { try { const url = ENDPOINTS.metrics(apiBase, metricId) @@ -92,27 +93,23 @@ async function fetchPlayers(apiBase: string, metricId: number): Promise const json: unknown = await res.json() return parseMetricPoints(json) } catch (err) { - console.warn('[GameStatus] fetch metrics échoué', err) + console.warn('[GameStatus] Metrics fetch failed', err) return 0 } } -// ─── Disponibilité : ping /version ─────────────────────────────────────────── - +/** Returns true when the `/version` endpoint responds successfully. */ async function pingAvailability(apiBase: string): Promise { try { - const res = await fetchWithTimeout(ENDPOINTS.version(apiBase), 5_000) + const res = await fetchWithTimeout(ENDPOINTS.version(apiBase), HTTP_TIMEOUT_MS.versionPing) return res.ok } catch { return false } } -// ─── IPC Handlers ───────────────────────────────────────────────────────────── - +/** Registers IPC handlers for env availability and server status. */ export function registerGameStatusHandlers(): void { - - // ── Vérification de disponibilité au démarrage ───────────────────────────── ipcMain.removeHandler('env:check-availability') ipcMain.handle('env:check-availability', async (): Promise> => { const envs: Env[] = ['universe', 'universe-testing'] @@ -120,18 +117,17 @@ export function registerGameStatusHandlers(): void { const results = await Promise.all( envs.map(async (env) => { const base = getApiBase(env) - if (!base) return false // var vide → indisponible immédiatement + if (!base) return false return pingAvailability(base) }) ) return { - 'universe': results[0], + universe: results[0], 'universe-testing': results[1] } }) - // ── Statut serveur + joueurs pour un env ─────────────────────────────────── ipcMain.removeHandler('game:get-server-status') ipcMain.handle('game:get-server-status', async (_event, env: Env): Promise => { const base = getApiBase(env) @@ -143,7 +139,6 @@ export function registerGameStatusHandlers(): void { const componentId = getStatusComponentId(env) const metricId = getStatusMetricId(env) - // Fetch status + players en parallèle const [{ status, label }, players] = await Promise.all([ fetchStatus(base, componentId), fetchPlayers(base, metricId) @@ -151,4 +146,4 @@ export function registerGameStatusHandlers(): void { return { status, statusLabel: label, players, available: true } }) -} \ No newline at end of file +} diff --git a/src/main/services/godotUserdataCache.ts b/src/main/services/godotUserdataCache.ts index ca6fd9f..bac132e 100644 --- a/src/main/services/godotUserdataCache.ts +++ b/src/main/services/godotUserdataCache.ts @@ -5,10 +5,10 @@ import * as path from 'path' const CACHE_DIR_NAMES = ['shader_cache', 'chunk_cache'] as const /** - * Dossier userdata Godot pour le projet « DyingStar » (même logique que le jeu). - * - Linux : ~/.local/share/godot/app_userdata/DyingStar - * - Windows : %AppData%/Godot/app_userdata/DyingStar (Roaming) - * - macOS : ~/Library/Application Support/Godot/app_userdata/DyingStar + * Godot userdata root for the DyingStar project (same layout as the game). + * - Linux: ~/.local/share/godot/app_userdata/DyingStar + * - Windows: %AppData%/Godot/app_userdata/DyingStar + * - macOS: ~/Library/Application Support/Godot/app_userdata/DyingStar */ export function getDyingStarGodotUserdataRoot(): string { if (process.platform === 'linux') { @@ -17,19 +17,20 @@ export function getDyingStarGodotUserdataRoot(): string { if (process.platform === 'win32' || process.platform === 'darwin') { return path.join(app.getPath('appData'), 'Godot', 'app_userdata', 'DyingStar') } - throw new Error(`Plateforme non supportée pour le cache Godot : ${process.platform}`) + throw new Error(`Unsupported platform for Godot cache: ${process.platform}`) } export type ClearGodotCacheResult = { - root: string - /** Chemins de dossiers effectivement supprimés */ - removed: string[] - /** Dossiers de cache absents (rien à faire) */ - skipped: string[] - /** Erreurs par chemin */ - errors: { path: string; message: string }[] + root: string + /** Directories that were removed */ + removed: string[] + /** Cache directories that were already absent */ + skipped: string[] + /** Per-path errors */ + errors: { path: string; message: string }[] } +/** Returns true when the path exists and is a directory. */ function isExistingDirectory(p: string): boolean { try { if (!fs.existsSync(p)) return false @@ -40,8 +41,8 @@ function isExistingDirectory(p: string): boolean { } /** - * Supprime uniquement `shader_cache` et `chunk_cache` sous le userdata DyingStar. - * Ne supprime que si le chemin existe et est un dossier (sinon ignoré, sans erreur bloquante). + * Removes only `shader_cache` and `chunk_cache` under DyingStar userdata. + * Missing paths are skipped without throwing. */ export function clearDyingStarGodotCaches(): ClearGodotCacheResult { const root = getDyingStarGodotUserdataRoot() @@ -49,30 +50,24 @@ export function clearDyingStarGodotCaches(): ClearGodotCacheResult { const skipped: string[] = [] const errors: { path: string; message: string }[] = [] - if (!isExistingDirectory(root)) { - for (const name of CACHE_DIR_NAMES) { - skipped.push(path.join(root, name)) + for (const dirName of CACHE_DIR_NAMES) { + const target = path.join(root, dirName) + if (!isExistingDirectory(target)) { + skipped.push(target) + continue } - return { root, removed, skipped, errors } - } - - for (const name of CACHE_DIR_NAMES) { - const dir = path.join(root, name) try { - if (!isExistingDirectory(dir)) { - skipped.push(dir) - continue - } - // Dernière vérif avant suppression (évite rm sur chemin disparu / concurrent) - if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) { - skipped.push(dir) + if (!isExistingDirectory(target)) { + skipped.push(target) continue } - fs.rmSync(dir, { recursive: true, force: true }) - removed.push(dir) - } catch (e) { - const message = e instanceof Error ? e.message : String(e) - errors.push({ path: dir, message }) + fs.rmSync(target, { recursive: true, force: true }) + removed.push(target) + } catch (err) { + errors.push({ + path: target, + message: err instanceof Error ? err.message : String(err) + }) } } diff --git a/src/main/services/version.ts b/src/main/services/version.ts index 65c2398..7842b33 100644 --- a/src/main/services/version.ts +++ b/src/main/services/version.ts @@ -1,10 +1,9 @@ import { ipcMain, app } from 'electron' import { getApiBase, ENDPOINTS } from '../config/api' +import { HTTP_TIMEOUT_MS } from '../config/constants' import type { Env } from '@shared/types/env' import type { GameVersionInfo, VersionCheckResult } from '@shared/types/version' -// ─── Types internes (API distantes) ─────────────────────────────────────────── - interface RemoteVersionPayload { version: string releaseDate: string @@ -20,36 +19,33 @@ interface LauncherReleaseInfo { releaseDate: string | null } -// ─── Helpers ────────────────────────────────────────────────────────────────── - -/** Compare semver / tags du launcher (ex. 0.2.0 vs 0.10.1). */ +/** Returns true when the remote launcher semver is newer than the local one. */ function isNewerLauncherVersion(remote: string, local: string): boolean { return remote.localeCompare(local, undefined, { numeric: true, sensitivity: 'base' }) > 0 } +/** Fetches remote game version metadata for an environment. */ export async function fetchRemoteGameVersion(env: Env): Promise { const base = getApiBase(env) if (!base) return null return fetchGameVersion(ENDPOINTS.version(base)) } +/** Fetches game version JSON from a URL. */ async function fetchGameVersion(url: string): Promise { try { const res = await fetch(url, { - signal: AbortSignal.timeout(8_000), + signal: AbortSignal.timeout(HTTP_TIMEOUT_MS.api), headers: { 'Cache-Control': 'no-cache' } }) if (!res.ok) return null - return await res.json() as RemoteVersionPayload + return (await res.json()) as RemoteVersionPayload } catch { return null } } -/** - * Déduit owner/repo depuis une URL de dépôt GitHub publique. - * Ex. https://github.com/DyingStar-game/launcher → DyingStar-game/launcher - */ +/** Parses `owner/repo` from a public GitHub repository page URL. */ function parseGithubRepoUrl(repoPageUrl: string): { owner: string; repo: string } | null { try { const u = new URL(repoPageUrl.trim()) @@ -63,6 +59,7 @@ function parseGithubRepoUrl(repoPageUrl: string): { owner: string; repo: string } } +/** Fetches the latest GitHub release for the launcher repository. */ async function fetchLatestLauncherReleaseFromGithub( repoPageUrl: string ): Promise { @@ -74,24 +71,22 @@ async function fetchLatestLauncherReleaseFromGithub( try { const res = await fetch(apiUrl, { - signal: AbortSignal.timeout(10_000), + signal: AbortSignal.timeout(HTTP_TIMEOUT_MS.github), headers: { - Accept: 'application/vnd.github+json', + Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', - 'User-Agent': ua + 'User-Agent': ua } }) - // Aucune release publique → pas de bandeau if (res.status === 404) return null if (!res.ok) return null - const data = await res.json() as GitHubLatestReleasePayload + const data = (await res.json()) as GitHubLatestReleasePayload const rawTag = data.tag_name?.trim() ?? '' if (!rawTag) return null const version = rawTag.startsWith('v') || rawTag.startsWith('V') ? rawTag.slice(1) : rawTag - const releaseDate = - data.published_at?.match(/^(\d{4}-\d{2}-\d{2})/)?.[1] ?? null + const releaseDate = data.published_at?.match(/^(\d{4}-\d{2}-\d{2})/)?.[1] ?? null return { version, releaseDate } } catch { @@ -99,25 +94,21 @@ async function fetchLatestLauncherReleaseFromGithub( } } -// ─── Handler IPC ────────────────────────────────────────────────────────────── - +/** Registers the IPC handler that checks launcher and game versions. */ export function registerVersionHandlers(): void { - ipcMain.handle('version:check', async (): Promise => { const currentLauncherVersion = app.getVersion() const githubRepoUrl = (import.meta.env.VITE_LAUNCHER_GITHUB_REPO_URL ?? '').trim() const envs: Env[] = ['universe', 'universe-testing'] - const payloads = await Promise.all(envs.map((env) => fetchRemoteGameVersion(env))) const toInfo = (p: RemoteVersionPayload | null): GameVersionInfo => ({ - version: p?.version ?? null, + version: p?.version ?? null, releaseDate: p?.releaseDate ?? null }) - // ── Launcher : dernière release GitHub (pas /version du jeu) - let latestLauncherVersion: string | null = null + let latestLauncherVersion: string | null = null let latestLauncherReleaseDate: string | null = null let launcherUpdateAvailable = false @@ -136,7 +127,7 @@ export function registerVersionHandlers(): void { latestLauncherReleaseDate, launcherUpdateAvailable, latestGameVersions: { - 'universe': toInfo(payloads[0]), + universe: toInfo(payloads[0]), 'universe-testing': toInfo(payloads[1]) } } diff --git a/src/main/services/window.ts b/src/main/services/window.ts index 1d89d65..2b5ecdc 100644 --- a/src/main/services/window.ts +++ b/src/main/services/window.ts @@ -1,15 +1,12 @@ import { ipcMain, BrowserWindow } from 'electron' +import { parsePositiveInt } from '../config/constants' -function parsePositiveInt(raw: string | undefined, fallback: number): number { - const n = Number(raw) - return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback -} - -const MIN_WIDTH = parsePositiveInt(import.meta.env.VITE_WINDOW_WIDTH, 1200) +const MIN_WIDTH = parsePositiveInt(import.meta.env.VITE_WINDOW_WIDTH, 1200) const MIN_HEIGHT = parsePositiveInt(import.meta.env.VITE_WINDOW_HEIGHT, 800) -const MAX_WIDTH = 1920 -const MAX_HEIGHT = 1200 +const MAX_WIDTH = parsePositiveInt(import.meta.env.VITE_WINDOW_MAX_WIDTH, 1920) +const MAX_HEIGHT = parsePositiveInt(import.meta.env.VITE_WINDOW_MAX_HEIGHT, 1200) +/** Registers IPC handlers for frameless window controls and auto-resize. */ export function registerWindowHandlers(getWindow: () => BrowserWindow | null): void { ipcMain.removeHandler('window:minimize') ipcMain.removeHandler('window:close') diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index ada397d..b0807f2 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -2,15 +2,18 @@ import { ElectronAPI } from '@electron-toolkit/preload' import type { Env } from '@shared/types/env' import type { UserInfo } from '@shared/types/auth' import type { InstallResult } from '@shared/types/install' -import type { GameVersionInfo, VersionCheckResult } from '@shared/types/version' +import type { VersionCheckResult } from '@shared/types/version' import type { ServerStatusValue } from '@shared/types/game' +import type { InstallProgressLabel } from '@shared/types/installProgress' +/** Server status payload returned by `getServerStatus`. */ interface ServerStatusResult { status: ServerStatusValue players: number available: boolean } +/** Auth event pushed from main after OAuth callback or error. */ type AuthStateChangedPayload = | { env: Env; status: 'connected'; user: UserInfo } | { env: Env; status: 'error'; error: string } @@ -18,15 +21,13 @@ type AuthStateChangedPayload = declare global { interface Window { electron: ElectronAPI + /** Typed bridge to main-process IPC handlers (see `preload/index.ts`). */ api: { selectDirectory: () => Promise installGame: (env: Env, installPath: string) => Promise - onInstallProgress: (callback: (progress: number, label: string) => void) => void + onInstallProgress: (callback: (progress: number, label: InstallProgressLabel) => void) => void readChangelog: (installPath: string) => Promise - resolveInstalledVersion: ( - env: Env, - installPath: string - ) => Promise + resolveInstalledVersion: (env: Env, installPath: string) => Promise clearGodotGameCache: () => Promise<{ root: string removed: string[] diff --git a/src/preload/index.ts b/src/preload/index.ts index 9788b44..b29e429 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,34 +1,42 @@ import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' import type { Env } from '@shared/types/env' +import type { InstallProgressLabel } from '@shared/types/installProgress' import type { UserInfo } from '@shared/types/auth' import type { ServerStatusValue } from '@shared/types/game' +/** + * Renderer-facing API exposed as `window.api`. + * Each method maps to an `ipcMain.handle` in the main process. + */ const api = { - // ── Fichiers / Installation ──────────────────────────────────────────────── - - selectDirectory: (): Promise => - ipcRenderer.invoke('files:select-directory'), + /** Opens a native folder picker; returns the chosen path or null if cancelled. */ + selectDirectory: (): Promise => ipcRenderer.invoke('files:select-directory'), + /** Downloads and extracts the game ZIP; returns installed version metadata. */ installGame: (env: Env, installPath: string): Promise<{ version: string; releaseDate: string }> => ipcRenderer.invoke('files:install', env, installPath), - onInstallProgress: (callback: (progress: number, label: string) => void): void => { + /** Subscribes to install/download progress events (0–100 and structured label). */ + onInstallProgress: (callback: (progress: number, label: InstallProgressLabel) => void): void => { ipcRenderer.removeAllListeners('files:progress') - ipcRenderer.on('files:progress', (_event, progress: number, label: string) => { + ipcRenderer.on('files:progress', (_event, progress: number, label: InstallProgressLabel) => { callback(progress, label) }) }, + /** Reads CHANGELOG.md from the install directory, if present. */ readChangelog: (installPath: string): Promise => ipcRenderer.invoke('files:read-changelog', installPath), + /** Re-resolves installed version from disk and remote API after install or on demand. */ resolveInstalledVersion: ( env: Env, installPath: string ): Promise<{ version: string; releaseDate: string } | null> => ipcRenderer.invoke('files:resolve-installed-version', env, installPath), + /** Clears Godot shader/chunk caches under the DyingStar userdata folder. */ clearGodotGameCache: (): Promise<{ root: string removed: string[] @@ -36,14 +44,14 @@ const api = { errors: { path: string; message: string }[] }> => ipcRenderer.invoke('files:clear-godot-cache'), - // ── Jeu ─────────────────────────────────────────────────────────────────── - + /** Spawns the game executable with a fresh JWT (`--token=`). */ launchGame: (env: Env, installPath: string): Promise => ipcRenderer.invoke('game:launch', env, installPath), - isGameRunning: (): Promise => - ipcRenderer.invoke('game:is-running'), + /** Returns whether a game process launched by the launcher is still running. */ + isGameRunning: (): Promise => ipcRenderer.invoke('game:is-running'), + /** Subscribes to changes in game running state (launch / exit). */ onGameRunningChanged: (callback: (running: boolean) => void): void => { ipcRenderer.removeAllListeners('game:running-changed') ipcRenderer.on('game:running-changed', (_event, payload: { running: boolean }) => { @@ -51,49 +59,53 @@ const api = { }) }, - getServerStatus: (env: Env): Promise<{ status: ServerStatusValue; players: number; available: boolean }> => + /** Fetches server status and connected player count for an environment. */ + getServerStatus: ( + env: Env + ): Promise<{ status: ServerStatusValue; players: number; available: boolean }> => ipcRenderer.invoke('game:get-server-status', env), - // ── Disponibilité des envs (ping au démarrage) ──────────────────────────── - + /** Pings each env API `/version` to determine runtime availability at startup. */ checkEnvAvailability: (): Promise> => ipcRenderer.invoke('env:check-availability'), + /** Quits the entire application. */ quitApp: (): Promise => ipcRenderer.invoke('app:quit'), + /** Minimizes the main window (frameless chrome). */ minimizeWindow: (): Promise => ipcRenderer.invoke('window:minimize'), + /** Closes the main window. */ closeWindow: (): Promise => ipcRenderer.invoke('window:close'), + /** Resizes the window to fit renderer content within min/max bounds. */ fitWindowToContent: (size: { width: number; height: number }): Promise => ipcRenderer.invoke('window:fit-content', size), - // ── Versions ────────────────────────────────────────────────────────────── - + /** Compares launcher and game versions against remote sources. */ checkVersions: (): Promise<{ - currentLauncherVersion: string - latestLauncherVersion: string | null + currentLauncherVersion: string + latestLauncherVersion: string | null latestLauncherReleaseDate: string | null - launcherUpdateAvailable: boolean - latestGameVersions: Record - }> => - ipcRenderer.invoke('version:check'), - - // ── Auth ────────────────────────────────────────────────────────────────── + launcherUpdateAvailable: boolean + latestGameVersions: Record + }> => ipcRenderer.invoke('version:check'), - authLogin: (env: Env): Promise => - ipcRenderer.invoke('auth:login', env), + /** Opens the system browser on the Keycloak Discord login page. */ + authLogin: (env: Env): Promise => ipcRenderer.invoke('auth:login', env), - authLogout: (env: Env): Promise => - ipcRenderer.invoke('auth:logout', env), + /** Clears stored tokens and opens Keycloak logout. */ + authLogout: (env: Env): Promise => ipcRenderer.invoke('auth:logout', env), - authLoadUser: (env: Env): Promise => - ipcRenderer.invoke('auth:load-user', env), + /** Refreshes the session and returns the user profile, or null if logged out. */ + authLoadUser: (env: Env): Promise => ipcRenderer.invoke('auth:load-user', env), + /** Subscribes to auth state changes after login, logout, or OAuth callback. */ onAuthStateChanged: ( - callback: (data: - | { env: Env; status: 'connected'; user: UserInfo } - | { env: Env; status: 'error'; error: string } + callback: ( + data: + | { env: Env; status: 'connected'; user: UserInfo } + | { env: Env; status: 'error'; error: string } ) => void ): void => { ipcRenderer.removeAllListeners('auth:state-changed') @@ -101,6 +113,7 @@ const api = { } } +/** Exposes safe APIs to the renderer (isolated context) or assigns to window in dev. */ if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) @@ -109,8 +122,8 @@ if (process.contextIsolated) { console.error('[Preload]', error) } } else { - // @ts-ignore + // @ts-expect-error non-isolated fallback assigns globals on window for debugging window.electron = electronAPI - // @ts-ignore + // @ts-expect-error non-isolated fallback assigns globals on window for debugging window.api = api -} \ No newline at end of file +} diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 4f8d8b0..61bbe75 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -3,19 +3,23 @@ import { useEffect } from 'react' import { useVersionStore } from '@stores/version' import { useAvailabilityStore } from '@stores/availability' +/** + * Runs one-time startup checks: API availability and version comparison. + */ function useAppStartup(): void { const { checkVersions, checked: versionChecked } = useVersionStore() const { checkAvailability, checked: availChecked } = useAvailabilityStore() useEffect(() => { - if (!availChecked) checkAvailability() - if (!versionChecked) checkVersions() - }, []) + if (!availChecked) void checkAvailability() + if (!versionChecked) void checkVersions() + }, [availChecked, versionChecked, checkAvailability, checkVersions]) } +/** Root React tree: shell layout and startup side effects. */ function App(): React.JSX.Element { useAppStartup() return } -export default App \ No newline at end of file +export default App diff --git a/src/renderer/assets/main.css b/src/renderer/assets/main.css index 6612bb3..ac73dab 100644 --- a/src/renderer/assets/main.css +++ b/src/renderer/assets/main.css @@ -1,4 +1,4 @@ -@import "tailwindcss"; +@import 'tailwindcss'; @font-face { font-family: 'Poppins'; @@ -32,22 +32,30 @@ font-display: swap; } -/* Tokens couleurs DyingStar — à ajuster avec la vraie charte */ @theme { --font-sans: 'Poppins', system-ui, sans-serif; - --color-ds-bg: #0d0d14; + + --color-ds-bg: #0d0d14; --color-ds-surface: #16161f; - --color-ds-surface-hover: rgba(255,255,255,0.05); - --color-ds-border: #2a2a3a; - --color-ds-accent: rgb(255, 186, 8,0.8); - --color-ds-text: rgb(255, 186, 8); - --color-ds-muted: #6b6b80; + --color-ds-surface-hover: rgba(255, 255, 255, 0.05); + --color-ds-surface-elevated: rgba(255, 255, 255, 0.03); + --color-ds-border: #2a2a3a; + --color-ds-accent: rgb(255, 186, 8, 0.8); + --color-ds-text: rgb(255, 186, 8); + --color-ds-muted: #6b6b80; --color-ds-success: #22c55e; --color-ds-warning: #f59e0b; - --color-ds-danger: #ef4444; + --color-ds-danger: #ef4444; + + --shadow-ds-panel: 0 12px 32px rgba(0, 0, 0, 0.35); + --shadow-ds-card: 0 8px 24px rgba(0, 0, 0, 0.3); + --shadow-ds-modal: 0 24px 64px rgba(0, 0, 0, 0.55); + --radius-ds-panel: 0.75rem; } -html, body { +html, +body { + height: 100%; min-height: 100%; width: 100%; background-color: var(--color-ds-bg); @@ -57,13 +65,13 @@ html, body { } #root { + height: 100%; min-height: 100%; width: 100%; display: flex; flex-direction: column; } -/* Barre de titre custom (fenêtre sans cadre OS) */ .app-drag { -webkit-app-region: drag; } @@ -72,6 +80,83 @@ html, body { -webkit-app-region: no-drag; } +@layer components { + .ds-panel { + background-color: var(--color-ds-surface); + border: 1px solid var(--color-ds-border); + border-radius: var(--radius-ds-panel); + box-shadow: var(--shadow-ds-panel); + transition: border-color 0.15s ease; + } + + .ds-panel:hover { + border-color: color-mix(in srgb, var(--color-ds-accent) 40%, transparent); + } + + /** Top bar: same surface as panels but no accent border on hover (avoids yellow outline when hovering child buttons). */ + .ds-panel-bar { + background-color: var(--color-ds-surface); + border-bottom: 1px solid var(--color-ds-border); + border-radius: 0; + box-shadow: none; + } + + .ds-panel-padded { + padding: 1.75rem; + } + + .ds-panel-padded-sm { + padding: 1.5rem; + } + + .ds-card { + background-color: var(--color-ds-surface); + border: 1px solid var(--color-ds-border); + border-radius: var(--radius-ds-panel); + box-shadow: var(--shadow-ds-card); + } + + .ds-modal { + background-color: var(--color-ds-surface); + border: 1px solid var(--color-ds-border); + border-radius: var(--radius-ds-panel); + box-shadow: var(--shadow-ds-modal); + } + + .ds-section-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.24em; + color: var(--color-ds-muted); + } + + .ds-sidebar { + width: 15rem; + flex-shrink: 0; + align-self: stretch; + min-height: 100%; + height: 100%; + border-right: 1px solid var(--color-ds-border); + background-color: color-mix(in srgb, var(--color-ds-surface) 30%, transparent); + backdrop-filter: blur(8px); + padding: 1rem 0.75rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + /** Full-height page shell (universe, social, lore). */ + .ds-page { + display: flex; + flex: 1 1 auto; + min-height: 0; + height: 100%; + width: 100%; + background-color: var(--color-ds-bg); + } +} + .lore-markdown { color: var(--color-ds-text); font-size: 15px; @@ -80,7 +165,6 @@ html, body { margin: 0 auto; } -/* TITRES */ .lore-markdown h1 { font-size: 2.4rem; font-weight: 800; @@ -102,24 +186,21 @@ html, body { color: var(--color-ds-muted); } -/* PARAGRAPHES */ .lore-markdown p { margin: 0.8rem 0; color: var(--color-ds-text); } -/* BLOCKQUOTE (style Medium) */ .lore-markdown blockquote { border-left: 3px solid var(--color-ds-accent); padding: 0.8rem 1rem; margin: 1.5rem 0; - background: rgba(255,255,255,0.03); + background: var(--color-ds-surface-elevated); border-radius: 8px; font-style: italic; color: var(--color-ds-muted); } -/* LISTES */ .lore-markdown ul { padding-left: 1.5rem; margin: 1rem 0; @@ -129,25 +210,23 @@ html, body { margin: 0.4rem 0; } -/* IMAGES */ .lore-markdown img { width: 100%; border-radius: 12px; margin: 1.5rem 0; - box-shadow: 0 10px 30px rgba(0,0,0,0.3); + box-shadow: var(--shadow-ds-panel); } -/* CODE BLOCK */ .lore-markdown code { - background: rgba(255,255,255,0.06); + background: var(--color-ds-surface-hover); padding: 2px 6px; border-radius: 6px; font-size: 0.9em; } .lore-markdown pre { - background: rgba(0,0,0,0.4); + background: rgba(0, 0, 0, 0.4); padding: 1rem; border-radius: 12px; overflow-x: auto; -} \ No newline at end of file +} diff --git a/src/renderer/components/layout/Navbar.tsx b/src/renderer/components/layout/Navbar.tsx index 9574e56..62e66a3 100644 --- a/src/renderer/components/layout/Navbar.tsx +++ b/src/renderer/components/layout/Navbar.tsx @@ -7,8 +7,10 @@ import type { View } from '@stores/navigation' import { useTranslation } from 'react-i18next' import Button from '@components/ui/primitives/Button' import DiscordIcon from '@components/ui/primitives/icons/DiscordIcon' +import { navUrls } from '@lib/env' import type { ReactNode } from 'react' +/** Wrapper for navbar icon buttons with hover border styling. */ function Icon({ children, className = '' @@ -21,7 +23,7 @@ function Icon({ className={[ 'inline-flex items-center justify-center w-9 h-9 rounded-lg', 'border border-[var(--color-ds-border)] bg-white/0', - 'hover:bg-[var(--color-ds-surface-hover)] hover:border-[var(--color-ds-accent)]/60', + 'hover:bg-[var(--color-ds-surface-hover)] hover:border-[var(--color-ds-border)]', 'transition-colors', className ].join(' ')} @@ -31,13 +33,8 @@ function Icon({ ) } -function Svg({ - children, - title -}: { - children: ReactNode - title: string -}): React.JSX.Element { +/** Accessible inline SVG with title for screen readers. */ +function Svg({ children, title }: { children: ReactNode; title: string }): React.JSX.Element { return (
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
@@ -245,7 +239,7 @@ export default function Navbar(): React.JSX.Element { : 'text-[var(--color-ds-muted)] hover:text-[var(--color-ds-text)] hover:bg-white/5' ].join(' ')} > - {env === 'universe' ? 'Universe' : 'Universe Testing'} + {env === 'universe' ? t('navbar.envUniverse') : t('navbar.envTesting')} ))}
@@ -273,20 +267,20 @@ export default function Navbar(): React.JSX.Element {
@@ -327,11 +321,11 @@ export default function Navbar(): React.JSX.Element { disabled={!navWiki} onClick={() => navWiki && window.open(navWiki, '_blank')} className="text-[var(--color-ds-muted)] hover:text-[var(--color-ds-text)] transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:text-[var(--color-ds-muted)]" - title={navWiki ? 'Wiki' : undefined} + title={navWiki ? t('navbar.brandWiki') : undefined} aria-label={t('navbar.openWiki')} > - + @@ -355,7 +349,9 @@ export default function Navbar(): React.JSX.Element {
) -} \ No newline at end of file +} diff --git a/src/renderer/components/layout/Shell.tsx b/src/renderer/components/layout/Shell.tsx index af5e863..3114573 100644 --- a/src/renderer/components/layout/Shell.tsx +++ b/src/renderer/components/layout/Shell.tsx @@ -5,26 +5,31 @@ import UniverseView from '@views/UniverseView' import Social from '@views/Social' import Lore from '@views/Lore' +/** Application shell: navbar, routed main content, and window auto-resize. */ export default function Shell(): React.JSX.Element { const { currentView } = useNavigationStore() useFitWindowToContent() + /** Maps navigation store view id to the corresponding page component. */ const renderView = (): React.JSX.Element => { switch (currentView) { - case 'universe': return - case 'universe-testing': return - case 'social': return - case 'lore': return - default: return + case 'universe': + return + case 'universe-testing': + return + case 'social': + return + case 'lore': + return + default: + return } } return ( -
+
-
- {renderView()} -
+
{renderView()}
) } diff --git a/src/renderer/components/panel/AccountPanel.tsx b/src/renderer/components/panel/AccountPanel.tsx index 523207d..f8dbfe4 100644 --- a/src/renderer/components/panel/AccountPanel.tsx +++ b/src/renderer/components/panel/AccountPanel.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' import Button from '@components/ui/primitives/Button' import DiscordIcon from '@components/ui/primitives/icons/DiscordIcon' +/** Account panel: Discord login/logout and session display per environment. */ export default function AccountPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() const { data, login, logout, cancelLogin } = useAccountStore() @@ -15,31 +16,22 @@ export default function AccountPanel(): React.JSX.Element { const { t } = useTranslation() return ( -
- +
-

- {t('universe.account.title')} -

+

{t('universe.account.title')}

{!isAvailable && ( -

- {t('universe.account.unavailable')} -

+

{t('universe.account.unavailable')}

)} {isAvailable && status === 'disconnected' && ( -

- {t('universe.account.disconnected')} -

+

{t('universe.account.disconnected')}

)} {isAvailable && status === 'loading' && ( -

- {t('universe.account.loading')} -

+

{t('universe.account.loading')}

)} {isAvailable && status === 'connected' && ( @@ -49,7 +41,6 @@ export default function AccountPanel(): React.JSX.Element { )}
- {!isAvailable && ( )} @@ -212,4 +205,4 @@ export default function FilesPanel(): React.JSX.Element { />
) -} \ No newline at end of file +} diff --git a/src/renderer/components/panel/GamePanel.tsx b/src/renderer/components/panel/GamePanel.tsx index 944b522..e66545a 100644 --- a/src/renderer/components/panel/GamePanel.tsx +++ b/src/renderer/components/panel/GamePanel.tsx @@ -2,8 +2,9 @@ import { useEffect } from 'react' import { useGameStore } from '@stores/game' import { useFilesStore } from '@stores/files' import { useVersionStore } from '@stores/version' -import { useEnvStore, type Env } from '@stores/env' +import { useEnvStore } from '@stores/env' import { isGameUpdateAvailable } from '@lib/isGameUpdateAvailable' +import { pollIntervalMinutes, statusPageUrlForEnv } from '@lib/env' import { useAvailabilityStore } from '@stores/availability' import { useAccountStore } from '@stores/account' import { useTranslation } from 'react-i18next' @@ -11,23 +12,7 @@ import ServerStatus from '@components/ui/feedback/ServerStatus' import Button from '@components/ui/primitives/Button' import PanelMessage from '@components/ui/feedback/PanelMessage' -function statusPageUrlForEnv(env: Env): string { - const raw = - env === 'universe' - ? import.meta.env.VITE_STATUS_PAGE_UNIVERSE - : import.meta.env.VITE_STATUS_PAGE_TESTING - return (raw ?? '').trim() -} - -/** Minutes entre deux appels statut + joueurs ; borne pour éviter valeurs absurdes. */ -function pollIntervalMinutes(): number { - const raw = import.meta.env.VITE_SERVER_STATUS_POLL_MINUTES ?? '5' - let n = Number.parseFloat(String(raw).trim()) - if (!Number.isFinite(n) || n < 1) n = 5 - if (n > 24 * 60) n = 24 * 60 - return n -} - +/** Game panel: server status, player count, play button, and status page link. */ export default function GamePanel(): React.JSX.Element { const { activeEnv } = useEnvStore() const { data: gameData, fetchServerStatus, play, gameRunning } = useGameStore() @@ -61,7 +46,7 @@ export default function GamePanel(): React.JSX.Element { return () => window.clearInterval(id) }, [activeEnv, isAvailable, fetchServerStatus]) - /** Opérationnel, à jour, connecté — sinon bouton désactivé. */ + /** Play is enabled when server is up, game installed, authenticated, and not already running. */ const canPlay = installed && isAvailable && @@ -74,16 +59,13 @@ export default function GamePanel(): React.JSX.Element { const canOpenStatusPage = Boolean(statusPageUrl) return ( -
- +
-

- {t('universe.game.title')} -

+

{t('universe.game.title')}

- {/* Statut serveur */} + {/* Server status */}
@@ -91,19 +73,15 @@ export default function GamePanel(): React.JSX.Element {
- {/* Joueurs connectés */} + {/* Connected players */} {isAvailable && (

{t('universe.game.players', { count: players })}

)} - {/* Jeu non installé */} - {!installed && ( - - {t('universe.game.notInstalled')} - - )} + {/* Game not installed */} + {!installed && {t('universe.game.notInstalled')}} {/* Actions */}
@@ -114,35 +92,22 @@ export default function GamePanel(): React.JSX.Element { )} {installed && gameUpdateAvailable && ( - - {t('universe.game.playDisabledUpdate')} - + {t('universe.game.playDisabledUpdate')} )} {installed && gameRunning && ( - - {t('universe.game.playDisabledRunning')} - + {t('universe.game.playDisabledRunning')} )} {installed && isAvailable && accountStatus === 'loading' && ( - - {t('universe.game.playDisabledAuthLoading')} - + {t('universe.game.playDisabledAuthLoading')} )} {installed && isAvailable && accountStatus === 'disconnected' && ( - - {t('universe.game.playDisabledAuth')} - + {t('universe.game.playDisabledAuth')} )} - @@ -150,13 +115,11 @@ export default function GamePanel(): React.JSX.Element { variant="secondary" className="w-full" disabled={!canOpenStatusPage} - onClick={() => - canOpenStatusPage ? window.open(statusPageUrl, '_blank') : undefined - } + onClick={() => (canOpenStatusPage ? window.open(statusPageUrl, '_blank') : undefined)} > {t('universe.game.viewStatus')}
) -} \ No newline at end of file +} diff --git a/src/renderer/components/panel/SocialPanel.tsx b/src/renderer/components/panel/SocialPanel.tsx index e29b07f..7bdbb58 100644 --- a/src/renderer/components/panel/SocialPanel.tsx +++ b/src/renderer/components/panel/SocialPanel.tsx @@ -1,5 +1,3 @@ -// src/renderer/components/panel/socialPanel.tsx - import { useEffect } from 'react' import { useSocialStore } from '@stores/social' import { useEnvStore } from '@stores/env' @@ -8,6 +6,7 @@ import { useTranslation } from 'react-i18next' import FriendItem from '@components/ui/social/FriendItem' import Button from '@components/ui/primitives/Button' +/** Compact social preview on the universe grid (friends + request badge). */ export default function SocialPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() const { data, fetchAll } = useSocialStore() @@ -20,11 +19,10 @@ export default function SocialPanel(): React.JSX.Element { useEffect(() => { fetchAll() - }, [activeEnv]) // Recharge les amis à chaque changement d'env + }, [activeEnv, fetchAll]) return ( -
- +
{notifications > 0 && (
@@ -35,18 +33,14 @@ export default function SocialPanel(): React.JSX.Element { {/* Header */}
-

- {t('universe.social.title')} -

+

{t('universe.social.title')}

{/* Friend list */}
{friends.length === 0 && ( -

- {t('universe.social.noFriends')} -

+

{t('universe.social.noFriends')}

)} {visibleFriends.map((friend) => ( @@ -64,4 +58,4 @@ export default function SocialPanel(): React.JSX.Element {
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/feedback/PanelMessage.tsx b/src/renderer/components/ui/feedback/PanelMessage.tsx index 369a4e0..f44a94b 100644 --- a/src/renderer/components/ui/feedback/PanelMessage.tsx +++ b/src/renderer/components/ui/feedback/PanelMessage.tsx @@ -9,11 +9,12 @@ type Props = { } const STYLES: Record = { - info: 'border-sky-500/35 bg-sky-500/12 text-sky-100', + info: 'border-sky-500/35 bg-sky-500/12 text-sky-100', warning: 'border-amber-500/35 bg-amber-500/12 text-amber-100', - error: 'border-red-500/35 bg-red-500/12 text-red-200' + error: 'border-red-500/35 bg-red-500/12 text-red-200' } +/** Inline info/warning/error message box used inside universe panels. */ export default function PanelMessage({ variant, children, diff --git a/src/renderer/components/ui/feedback/ServerStatus.tsx b/src/renderer/components/ui/feedback/ServerStatus.tsx index a12ed64..ddcd6ee 100644 --- a/src/renderer/components/ui/feedback/ServerStatus.tsx +++ b/src/renderer/components/ui/feedback/ServerStatus.tsx @@ -3,23 +3,26 @@ import type { ServerStatusValue } from '@stores/game' type Props = { status: ServerStatusValue } const STATUS_CONFIG: Record = { - online: { color: 'bg-emerald-400', pulse: true }, - degraded: { color: 'bg-amber-400', pulse: true }, - offline: { color: 'bg-red-500', pulse: false }, - maintenance: { color: 'bg-sky-400', pulse: true }, - unknown: { color: 'bg-zinc-500', pulse: false }, - unavailable: { color: 'bg-zinc-600', pulse: false }, + online: { color: 'bg-emerald-400', pulse: true }, + degraded: { color: 'bg-amber-400', pulse: true }, + offline: { color: 'bg-red-500', pulse: false }, + maintenance: { color: 'bg-sky-400', pulse: true }, + unknown: { color: 'bg-zinc-500', pulse: false }, + unavailable: { color: 'bg-zinc-600', pulse: false } } +/** Colored status dot with optional pulse animation for live server state. */ export default function ServerStatus({ status }: Props): React.JSX.Element { const { color, pulse } = STATUS_CONFIG[status] ?? STATUS_CONFIG.unknown return ( {pulse && ( - + )} ) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/feedback/UpdateAlert.tsx b/src/renderer/components/ui/feedback/UpdateAlert.tsx index dcab7a0..49e69c1 100644 --- a/src/renderer/components/ui/feedback/UpdateAlert.tsx +++ b/src/renderer/components/ui/feedback/UpdateAlert.tsx @@ -3,17 +3,22 @@ import { useTranslation } from 'react-i18next' type Variant = 'launcher' | 'game' type Props = { - variant: Variant - currentVersion?: string - latestVersion: string + variant: Variant + currentVersion?: string + latestVersion: string latestReleaseDate?: string - onDismiss?: () => void + onDismiss?: () => void } const CONFIG = { launcher: { icon: ( - + @@ -22,7 +27,12 @@ const CONFIG = { }, game: { icon: ( - + @@ -34,25 +44,24 @@ const CONFIG = { const COLOR_CLASSES = { amber: { wrapper: 'border-amber-500/30 bg-amber-500/8', - icon: 'text-amber-400', - title: 'text-amber-300', - text: 'text-sky-200/70', - badge: 'border-amber-500/30 bg-amber-500/15 text-amber-300', + icon: 'text-amber-400', + title: 'text-amber-300', + text: 'text-sky-200/70', + badge: 'border-amber-500/30 bg-amber-500/15 text-amber-300', dismiss: 'text-amber-400/50 hover:text-amber-300' }, sky: { wrapper: 'border-sky-500/30 bg-sky-500/8', - icon: 'text-sky-400', - title: 'text-sky-300', - text: 'text-sky-200/70', - badge: 'border-sky-500/30 bg-sky-500/15 text-sky-300', + icon: 'text-sky-400', + title: 'text-sky-300', + text: 'text-sky-200/70', + badge: 'border-sky-500/30 bg-sky-500/15 text-sky-300', dismiss: 'text-sky-400/50 hover:text-sky-300' } } /** - * Formate un timestamp YYYYMMDDHHMMSS en "10/05/2026 09:29" - * Laisse passer les autres formats tels quels (semver, etc.) + * Formats YYYYMMDDHHMMSS build ids as "DD/MM/YYYY HH:mm"; passes through semver as-is. */ function formatVersion(v: string): string { if (v.length === 14 && /^\d+$/.test(v)) { @@ -63,6 +72,7 @@ function formatVersion(v: string): string { return v } +/** Banner when a newer launcher or game build is available remotely. */ export default function UpdateAlert({ variant, currentVersion, @@ -82,16 +92,15 @@ export default function UpdateAlert({ const discordLauncherUrl = (import.meta.env.VITE_LAUNCHER_RELEASE_DISCORD_URL ?? '').trim() return ( -
- +
{/* Icône */} {cfg.icon} {/* Texte */}
- - {title} - + {title}
@@ -99,11 +108,15 @@ export default function UpdateAlert({ {formatVersion(currentVersion)} )} {currentVersion && } - + {formatVersion(latestVersion)} {latestReleaseDate && ( - · {t('universe.updateAlert.releaseOn', { date: latestReleaseDate })} + + · {t('universe.updateAlert.releaseOn', { date: latestReleaseDate })} + )}
{variant === 'launcher' && discordLauncherUrl && ( @@ -126,14 +139,18 @@ export default function UpdateAlert({ {onDismiss && ( )}
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/lore/LoreArticle.tsx b/src/renderer/components/ui/lore/LoreArticle.tsx index 3a10ecf..f5aadf1 100644 --- a/src/renderer/components/ui/lore/LoreArticle.tsx +++ b/src/renderer/components/ui/lore/LoreArticle.tsx @@ -1,4 +1,6 @@ +import type React from 'react' import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLoreStore } from '@stores/lore' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' @@ -10,53 +12,53 @@ const loreMarkdownFiles = import.meta.glob('../../../content/lore/*.md', { import: 'default' }) as Record Promise> -export default function LoreArticle() { +/** Loads and renders the selected lore markdown article via Vite glob imports. */ +export default function LoreArticle(): React.JSX.Element { + const { t } = useTranslation() const { current } = useLoreStore() const [content, setContent] = useState('') - useEffect(() => { - if (!current) return + const articleKey = current ? `../../../content/lore/${current.file}` : null + const loader = articleKey ? loreMarkdownFiles[articleKey] : undefined + const missingFile = Boolean(current && articleKey && !loader) - const key = `../../../content/lore/${current.file}` - const loader = loreMarkdownFiles[key] + useEffect(() => { + if (!current || !loader) return - if (!loader) { - setContent(`⚠️ Fichier introuvable: ${current.file}`) - return + let cancelled = false + void loader().then((markdown) => { + if (!cancelled) setContent(markdown) + }) + return () => { + cancelled = true } - - loader().then(setContent) - }, [current]) + }, [current, loader]) if (!current) { return ( -
-
- Sélectionne un article du lore +
+
+ {t('lore.selectArticle')}
) } + const title = t(`lore.articles.${current.id}`, { defaultValue: current.id }) + const body = missingFile ? t('lore.fileNotFound', { file: current.file }) : content + return ( -
-
+
+
- - {/* TITLE (optionnel override) */} -

{current.title}

- - - {content} +

{title}

+ + {body} -
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/lore/LoreSidebar.tsx b/src/renderer/components/ui/lore/LoreSidebar.tsx index 6574572..f1c2458 100644 --- a/src/renderer/components/ui/lore/LoreSidebar.tsx +++ b/src/renderer/components/ui/lore/LoreSidebar.tsx @@ -1,17 +1,19 @@ +import type React from 'react' +import { useTranslation } from 'react-i18next' import { useLoreStore } from '@stores/lore' -export default function LoreSidebar() { +/** Sidebar list of lore articles from the lore store. */ +export default function LoreSidebar(): React.JSX.Element { + const { t } = useTranslation() const { articles, current, select } = useLoreStore() return ( -
+

- Lore -

-

- Articles, chroniques, archives. + {t('lore.sidebarTitle')}

+

{t('lore.sidebarSubtitle')}

{articles.map((a) => ( @@ -24,10 +26,9 @@ export default function LoreSidebar() { : 'text-[var(--color-ds-muted)] border-transparent hover:border-[var(--color-ds-border)] hover:bg-[var(--color-ds-surface-hover)] hover:text-[var(--color-ds-text)]' }`} > - {a.title} + {t(`lore.articles.${a.id}`, { defaultValue: a.id })} ))} -
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/overlays/ChangelogModal.tsx b/src/renderer/components/ui/overlays/ChangelogModal.tsx index 66530fc..5dc4159 100644 --- a/src/renderer/components/ui/overlays/ChangelogModal.tsx +++ b/src/renderer/components/ui/overlays/ChangelogModal.tsx @@ -1,18 +1,20 @@ import { useEffect } from 'react' +import { useTranslation } from 'react-i18next' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import rehypeSanitize from 'rehype-sanitize' type Props = { - open: boolean - title: string - loading?: boolean + open: boolean + title: string + loading?: boolean loadingLabel: string - markdown: string | null - emptyLabel: string - onClose: () => void + markdown: string | null + emptyLabel: string + onClose: () => void } +/** Modal that displays CHANGELOG.md from the game install directory. */ export default function ChangelogModal({ open, title, @@ -22,6 +24,8 @@ export default function ChangelogModal({ emptyLabel, onClose }: Props): React.JSX.Element | null { + const { t } = useTranslation() + useEffect(() => { if (!open) return const onKey = (e: KeyboardEvent): void => { @@ -44,29 +48,34 @@ export default function ChangelogModal({ }} >
e.stopPropagation()} >
-

+

{title}

- {loading && ( -

{loadingLabel}

- )} + {loading &&

{loadingLabel}

} {!loading && (markdown === null || markdown === '') && (

{emptyLabel}

)} diff --git a/src/renderer/components/ui/primitives/Button.tsx b/src/renderer/components/ui/primitives/Button.tsx index 890b372..2011719 100644 --- a/src/renderer/components/ui/primitives/Button.tsx +++ b/src/renderer/components/ui/primitives/Button.tsx @@ -34,6 +34,7 @@ const variants: Record = { 'hover:bg-[var(--color-ds-surface-hover)] hover:border-[var(--color-ds-border)] hover:text-[var(--color-ds-text)]' } +/** Themed button with primary/secondary/danger/ghost variants. */ export default function Button({ variant = 'secondary', size = 'md', @@ -49,4 +50,3 @@ export default function Button({ /> ) } - diff --git a/src/renderer/components/ui/primitives/InputField.tsx b/src/renderer/components/ui/primitives/InputField.tsx index afb5b4e..f7b2e95 100644 --- a/src/renderer/components/ui/primitives/InputField.tsx +++ b/src/renderer/components/ui/primitives/InputField.tsx @@ -15,6 +15,7 @@ type InputFieldProps = { readOnly?: boolean } +/** Labeled text input with optional action button (used in social forms). */ export default function InputField({ label, value, @@ -74,4 +75,4 @@ export default function InputField({
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/primitives/icons/DiscordIcon.tsx b/src/renderer/components/ui/primitives/icons/DiscordIcon.tsx index 2eaa9c6..76b5919 100644 --- a/src/renderer/components/ui/primitives/icons/DiscordIcon.tsx +++ b/src/renderer/components/ui/primitives/icons/DiscordIcon.tsx @@ -3,7 +3,11 @@ type Props = { title?: string } -export default function DiscordIcon({ className = 'w-4.5 h-4.5', title = 'Discord' }: Props): React.JSX.Element { +/** Discord logo SVG for login and navbar links. */ +export default function DiscordIcon({ + className = 'w-4.5 h-4.5', + title = 'Discord' +}: Props): React.JSX.Element { return (
- - {/* Avatar */}
{friend.name.charAt(0)}
- - {/* Status dot */}
- {/* Info */}
- - {friend.name} - + {friend.name} {friend.status === 'ingame' && ( - En jeu • {friend.game} + {t('friendStatus.ingameWithGame', { game: friend.game ?? '' })} )} {friend.status === 'online' && ( - - En ligne - + {t('friendStatus.online')} )} {friend.status === 'offline' && ( - - Hors ligne - + {t('friendStatus.offline')} )}
) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/social/FriendRow.tsx b/src/renderer/components/ui/social/FriendRow.tsx index 78d1280..ae4529c 100644 --- a/src/renderer/components/ui/social/FriendRow.tsx +++ b/src/renderer/components/ui/social/FriendRow.tsx @@ -1,10 +1,15 @@ +import { useTranslation } from 'react-i18next' + type FriendLike = { name: string status: string game?: string } +/** Friend list row with online/ingame/offline status indicator. */ export function FriendRow({ friend }: { friend: FriendLike }): React.JSX.Element { + const { t } = useTranslation() + const color = friend.status === 'online' ? 'bg-green-500' @@ -12,6 +17,15 @@ export function FriendRow({ friend }: { friend: FriendLike }): React.JSX.Element ? 'bg-orange-400' : 'bg-gray-500' + const statusLabel = + friend.status === 'ingame' && friend.game + ? t('friendStatus.ingameWithGame', { game: friend.game }) + : friend.status === 'online' + ? t('friendStatus.online') + : friend.status === 'offline' + ? t('friendStatus.offline') + : friend.status + return (
@@ -21,16 +35,11 @@ export function FriendRow({ friend }: { friend: FriendLike }): React.JSX.Element
-

{friend.name}

-

- {friend.status === 'ingame' && friend.game - ? `En jeu • ${friend.game}` - : friend.status} -

+

{statusLabel}

) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/social/OrgaRow.tsx b/src/renderer/components/ui/social/OrgaRow.tsx index 5bf881e..5418ff0 100644 --- a/src/renderer/components/ui/social/OrgaRow.tsx +++ b/src/renderer/components/ui/social/OrgaRow.tsx @@ -1,12 +1,16 @@ import { useState } from 'react' +import { useTranslation } from 'react-i18next' import type { Orga } from '@stores/social' +import { getSocialErrorMessage } from '@lib/socialErrors' type Props = { orga: Orga onJoin: () => Promise } +/** Organization row with member count and join button when not a member. */ export function OrgaRow({ orga, onJoin }: Props): React.JSX.Element { + const { t } = useTranslation() const [status, setStatus] = useState<'idle' | 'loading' | 'error'>('idle') const [errorMsg, setErrorMsg] = useState('') @@ -15,17 +19,15 @@ export function OrgaRow({ orga, onJoin }: Props): React.JSX.Element { setErrorMsg('') try { await onJoin() - // isMember sera passé à true via le store — pas de setStatus('done') nécessaire - // le composant sera re-rendu avec orga.isMember === true + // Store sets isMember to true — component re-renders with updated orga } catch (err) { setStatus('error') - setErrorMsg(err instanceof Error ? err.message : 'Erreur lors de la tentative.') + setErrorMsg(getSocialErrorMessage(err, t)) } } return (
- {/* Icône orga */}
@@ -37,32 +39,32 @@ export function OrgaRow({ orga, onJoin }: Props): React.JSX.Element { {orga.name} - {orga.members} membre{orga.members > 1 ? 's' : ''} + {t('universe.socialPage.orga.members', { count: orga.members })} - {status === 'error' && ( - {errorMsg} - )} + {status === 'error' && {errorMsg}}
{/* Badge membre ou bouton rejoindre */} {orga.isMember ? ( - + " + > - Membre + {t('universe.socialPage.orga.memberBadge')} ) : ( {/* Refuser */}
) } -// ─── Icônes SVG inline ──────────────────────────────────────────────────────── +// ─── Inline SVG icons ─────────────────────────────────────────────────────── function CheckIcon(): React.JSX.Element { return ( - + ) @@ -110,7 +108,12 @@ function CheckIcon(): React.JSX.Element { function XIcon(): React.JSX.Element { return ( - + ) @@ -118,9 +121,13 @@ function XIcon(): React.JSX.Element { function SpinnerIcon(): React.JSX.Element { return ( - + ) -} \ No newline at end of file +} diff --git a/src/renderer/components/ui/social/SocialSidebar.tsx b/src/renderer/components/ui/social/SocialSidebar.tsx index 9f4353a..ed1379a 100644 --- a/src/renderer/components/ui/social/SocialSidebar.tsx +++ b/src/renderer/components/ui/social/SocialSidebar.tsx @@ -1,28 +1,28 @@ +import { useTranslation } from 'react-i18next' + type Props = { current: string setCurrent: (v: string) => void } -export default function SocialSidebar({ - current, - setCurrent -}: Props) { - const items = ['friends', 'organizations', 'requests'] +const TAB_KEYS = ['friends', 'organizations', 'requests'] as const - return ( -
+/** Tab sidebar for the social page (friends / orgs / requests). */ +export default function SocialSidebar({ current, setCurrent }: Props): React.JSX.Element { + const { t } = useTranslation() + return ( +

- Social + {t('universe.socialPage.sidebarTitle')}

-

- Amis, organisations, demandes. + {t('universe.socialPage.sidebarSubtitle')}

- {items.map((item) => ( + {TAB_KEYS.map((item) => ( ))}
) -} \ No newline at end of file +} diff --git a/src/renderer/content/lore/origins.md b/src/renderer/content/lore/origins.md index bbfd2e7..0611b15 100644 --- a/src/renderer/content/lore/origins.md +++ b/src/renderer/content/lore/origins.md @@ -12,4 +12,4 @@ Ils dominaient la réalité avant la fracture. ## La fracture -> Un événement qui a changé l’univers à jamais. \ No newline at end of file +> Un événement qui a changé l’univers à jamais. diff --git a/src/renderer/hooks/useFitWindowToContent.ts b/src/renderer/hooks/useFitWindowToContent.ts index 18b13f2..012c2e7 100644 --- a/src/renderer/hooks/useFitWindowToContent.ts +++ b/src/renderer/hooks/useFitWindowToContent.ts @@ -1,17 +1,21 @@ import { useEffect } from 'react' -/** Agrandit la fenêtre si le contenu dépasse la taille de base (min = variables d'env). */ +/** + * Observes layout changes and asks the main process to resize the frameless window + * so content is not clipped (respects min/max dimensions from env). + */ export function useFitWindowToContent(): void { useEffect(() => { let rafId = 0 + /** Measures `#root` and invokes IPC resize on the next animation frame. */ const report = (): void => { cancelAnimationFrame(rafId) rafId = requestAnimationFrame(() => { const root = document.getElementById('root') if (!root) return void window.api.fitWindowToContent({ - width: root.scrollWidth, + width: root.scrollWidth, height: root.scrollHeight }) }) @@ -21,7 +25,12 @@ export function useFitWindowToContent(): void { ro.observe(document.body) const mo = new MutationObserver(report) - mo.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true }) + mo.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + characterData: true + }) report() diff --git a/src/renderer/i18n/en.json b/src/renderer/i18n/en.json index 499fbf4..a0e3636 100644 --- a/src/renderer/i18n/en.json +++ b/src/renderer/i18n/en.json @@ -1,102 +1,194 @@ -{ - "navbar": { - "home": "Home", - "social": "Social", - "lore": "Lore", - "support": "Donate", - "openWebsite": "Open official website", - "openDiscord": "Open Discord", - "openWiki": "Open wiki", - "donateUnavailable": "Support link not configured", - "quit": "Quit application", - "minimize": "Minimize", - "close": "Close" +{ + "installProgress": { + "connecting": "Connecting to server…", + "checkingUpdates": "Checking for updates…", + "downloading": "Downloading… ({{downloaded}} / {{total}})", + "downloadingIndeterminate": "Downloading… ({{downloaded}})", + "preparing": "Preparing game folder…", + "extracting": "Extracting… ({{current}} / {{total}} files)", + "cleaning": "Cleaning up…", + "completeInstall": "Installation complete — v{{version}}", + "completeUpdate": "Update complete — v{{version}}" + }, + "common": { + "cancel": "Cancel", + "loading": "...", + "close": "Close", + "send": "Send", + "create": "Create" + }, + "navbar": { + "home": "Home", + "social": "Social", + "lore": "Lore", + "support": "Donate", + "openWebsite": "Open official website", + "openDiscord": "Open Discord", + "openWiki": "Open wiki", + "donateUnavailable": "Support link not configured", + "quit": "Quit application", + "minimize": "Minimize", + "close": "Close", + "languageFr": "Français", + "languageEn": "English", + "switchToFrench": "Switch to French", + "switchToEnglish": "Switch to English", + "envUniverse": "Universe", + "envTesting": "Universe Testing", + "brandDiscord": "Discord", + "brandWiki": "Wiki" + }, + "universeTesting": { + "banner": "Testing environment — data is not from the live server" + }, + "lore": { + "sidebarTitle": "Lore", + "sidebarSubtitle": "Articles, chronicles, archives.", + "selectArticle": "Select a lore article", + "fileNotFound": "File not found: {{file}}", + "articles": { + "origins": "Origins" + } + }, + "friendStatus": { + "online": "Online", + "offline": "Offline", + "ingame": "In game", + "ingameWithGame": "In game • {{game}}" + }, + "universe": { + "title": "Universe", + "account": { + "title": "Account", + "disconnected": "Not connected", + "connectedAs": "Logged in as {{username}}", + "login": "Login with Discord", + "logout": "Logout", + "subscription": "Subscription", + "loading": "Connecting...", + "cancel": "Cancel", + "unavailable": "Authentication not available for this environment.", + "unavailableBtn": "Unavailable" }, - "universeTesting": { - "banner": "Testing environment — data is not from the live server" + "updateAlert": { + "dismiss": "Dismiss alert", + "launcher": { + "title": "Launcher update available", + "discordHint": "The new version is available here:", + "discordLink": "Discord" + }, + "game": { + "title": "New game version available" + }, + "releaseOn": "Released {{date}}" }, - "universe": { - "title": "Universe", - "account": { - "title": "Account", - "disconnected": "Not connected", - "connectedAs": "Logged in as {{username}}", - "login": "Login with Discord", - "logout": "Logout", - "subscription": "Subscription", - "loading": "Connecting...", - "cancel": "Cancel", - "unavailable": "Authentication not available for this environment.", - "unavailableBtn": "Unavailable" + "game": { + "title": "Game", + "play": "Play", + "playDisabledMaintenance": "The server is under maintenance — launching the game is disabled.", + "playDisabledUpdate": "A game update is available — install it before playing.", + "playDisabledAuth": "Sign in to launch the game.", + "playDisabledAuthLoading": "Checking your session…", + "playDisabledRunning": "The game is already running.", + "notInstalled": "Game not installed", + "players": "{{count}} players online", + "status": { + "online": "Available", + "degraded": "Issues ongoing", + "offline": "Offline", + "maintenance": "Under maintenance", + "unknown": "Unknown", + "unavailable": "Unavailable" }, - "updateAlert": { - "launcher": { - "title": "Launcher update available", - "discordHint": "The new version is available here:", - "discordLink": "Discord" + "viewStatus": "View status" + }, + "files": { + "title": "Game files", + "version": "Version: {{version}}", + "releaseDate": "Release: {{date}}", + "notInstalled": "Game not installed", + "install": "Install", + "update": "Update", + "verify": "Verify files", + "clearCache": "Clear cache", + "clearCacheToastOk": "Cache cleared.", + "clearCacheToastPartial": "Cache partially cleared (some folders could not be removed).", + "clearCacheToastError": "Could not clear the cache.", + "changelog": "View changelog", + "changelogModalTitle": "Changelog — v{{version}}", + "changelogLoading": "Loading changelog…", + "changelogMissing": "No CHANGELOG.md was found in the install folder (make sure it is included in the game archive).", + "installing": "Installing...", + "installPath": "Install folder", + "installPathPlaceholder": "/home/user/games/dyingstar", + "browse": "Browse", + "unavailable": "Installation not available for this environment.", + "unavailableBtn": "Unavailable" + }, + "social": { + "title": "Social", + "noFriends": "No friends", + "seeAll": "See all", + "notifications": "{{count}} notifications" + }, + "socialPage": { + "sidebarTitle": "Social", + "sidebarSubtitle": "Friends, organizations, requests.", + "friends": "Friends", + "organizations": "Organizations", + "requests": "Requests", + "addFriend": "Add friend", + "createOrga": "Create org", + "createOrgaLong": "Create organization", + "joinOrga": "Join", + "accept": "Accept", + "decline": "Decline", + "tabs": { + "friends": { + "label": "Friends", + "subtitle": "Manage your friends and status." }, - "game": { - "title": "New game version available" + "organizations": { + "label": "Organizations", + "subtitle": "Organizations, members, and invites." }, - "releaseOn": "Released {{date}}" + "requests": { + "label": "Requests", + "subtitle": "Pending friend requests." + } }, - "game": { - "title": "Game", - "play": "Play", - "playDisabledMaintenance": "The server is under maintenance — launching the game is disabled.", - "playDisabledUpdate": "A game update is available — install it before playing.", - "playDisabledAuth": "Sign in to launch the game.", - "playDisabledAuthLoading": "Checking your session…", - "playDisabledRunning": "The game is already running.", - "notInstalled": "Game not installed", - "players": "{{count}} players online", - "status": { - "online": "Available", - "degraded": "Issues ongoing", - "offline": "Offline", - "maintenance": "Under maintenance", - "unknown": "Unknown", - "unavailable": "Unavailable" - }, - "viewStatus": "View status" + "addFriendForm": { + "title": "Add a friend", + "usernameLabel": "Exact username", + "usernamePlaceholder": "Enter a username...", + "success": "Request sent!" + }, + "createOrgaForm": { + "title": "Create an organization", + "nameLabel": "Organization name", + "namePlaceholder": "Enter a unique name...", + "success": "Organization created!" }, - "files": { - "title": "Game files", - "version": "Version: {{version}}", - "releaseDate": "Release: {{date}}", - "notInstalled": "Game not installed", - "install": "Install", - "update": "Update", - "verify": "Verify files", - "clearCache": "Clear cache", - "clearCacheToastOk": "Cache cleared.", - "clearCacheToastPartial": "Cache partially cleared (some folders could not be removed).", - "clearCacheToastError": "Could not clear the cache.", - "changelog": "View changelog", - "changelogModalTitle": "Changelog — v{{version}}", - "changelogLoading": "Loading changelog…", - "changelogMissing": "No CHANGELOG.md was found in the install folder (make sure it is included in the game archive).", - "installing": "Installing...", - "installPath": "Install folder", - "browse": "Browse", - "unavailable": "Installation not available for this environment.", - "unavailableBtn": "Unavailable" + "pendingRequests_one": "{{count}} pending request", + "pendingRequests_other": "{{count}} pending requests", + "emptyFriends": "No friends yet.", + "emptyOrganizations": "No organizations.", + "emptyRequests": "No pending requests.", + "orga": { + "members_one": "{{count}} member", + "members_other": "{{count}} members", + "memberBadge": "Member", + "joinAria": "Join organization {{name}}" }, - "social": { - "title": "Social", - "noFriends": "No friends", - "seeAll": "See all", - "notifications": "{{count}} notifications" + "request": { + "acceptAria": "Accept request from {{name}}", + "declineAria": "Decline request from {{name}}" }, - "socialPage": { - "friends": "Friends", - "organizations": "Organizations", - "requests": "Requests", - "addFriend": "Add friend", - "createOrga": "Create organization", - "joinOrga": "Join organization", - "accept": "Accept", - "decline": "Decline" + "errors": { + "friendRequestFailed": "User not found or request already sent.", + "orgaNameTaken": "This organization name is already taken.", + "joinOrgaFailed": "Could not join this organization." } } - } \ No newline at end of file + } +} diff --git a/src/renderer/i18n/fr.json b/src/renderer/i18n/fr.json index 49d2a57..af40d76 100644 --- a/src/renderer/i18n/fr.json +++ b/src/renderer/i18n/fr.json @@ -1,102 +1,194 @@ { - "navbar": { - "home": "Accueil", - "social": "Social", - "lore": "Lore", - "support": "Faire un don", - "openWebsite": "Ouvrir le site officiel", - "openDiscord": "Ouvrir Discord", - "openWiki": "Ouvrir le wiki", - "donateUnavailable": "Lien de soutien non configuré", - "quit": "Quitter l'application", - "minimize": "Réduire", - "close": "Fermer" + "installProgress": { + "connecting": "Connexion au serveur…", + "checkingUpdates": "Recherche de mises à jour…", + "downloading": "Téléchargement… ({{downloaded}} / {{total}})", + "downloadingIndeterminate": "Téléchargement… ({{downloaded}})", + "preparing": "Préparation du dossier du jeu…", + "extracting": "Extraction… ({{current}} / {{total}} fichiers)", + "cleaning": "Nettoyage…", + "completeInstall": "Installation terminée — v{{version}}", + "completeUpdate": "Mise à jour terminée — v{{version}}" + }, + "common": { + "cancel": "Annuler", + "loading": "...", + "close": "Fermer", + "send": "Envoyer", + "create": "Créer" + }, + "navbar": { + "home": "Accueil", + "social": "Social", + "lore": "Lore", + "support": "Faire un don", + "openWebsite": "Ouvrir le site officiel", + "openDiscord": "Ouvrir Discord", + "openWiki": "Ouvrir le wiki", + "donateUnavailable": "Lien de soutien non configuré", + "quit": "Quitter l'application", + "minimize": "Réduire", + "close": "Fermer", + "languageFr": "Français", + "languageEn": "English", + "switchToFrench": "Passer en français", + "switchToEnglish": "Switch to English", + "envUniverse": "Universe", + "envTesting": "Universe Testing", + "brandDiscord": "Discord", + "brandWiki": "Wiki" + }, + "universeTesting": { + "banner": "Environnement de test — les données ne sont pas celles du serveur live" + }, + "lore": { + "sidebarTitle": "Lore", + "sidebarSubtitle": "Articles, chroniques, archives.", + "selectArticle": "Sélectionne un article du lore", + "fileNotFound": "Fichier introuvable : {{file}}", + "articles": { + "origins": "Origines" + } + }, + "friendStatus": { + "online": "En ligne", + "offline": "Hors ligne", + "ingame": "En jeu", + "ingameWithGame": "En jeu • {{game}}" + }, + "universe": { + "title": "Universe", + "account": { + "title": "Compte", + "disconnected": "Non connecté", + "connectedAs": "Connecté en tant que {{username}}", + "login": "Se connecter avec Discord", + "logout": "Se déconnecter", + "subscription": "Abonnement", + "loading": "Connexion en cours...", + "cancel": "Annuler", + "unavailable": "Authentification non disponible pour cet environnement.", + "unavailableBtn": "Indisponible" }, - "universeTesting": { - "banner": "Environnement de test — les données ne sont pas celles du serveur live" + "updateAlert": { + "dismiss": "Fermer l'alerte", + "launcher": { + "title": "Mise à jour du launcher disponible", + "discordHint": "La nouvelle version est disponible ici :", + "discordLink": "Discord" + }, + "game": { + "title": "Nouvelle version du jeu disponible" + }, + "releaseOn": "Sortie le {{date}}" }, - "universe": { - "title": "Universe", - "account": { - "title": "Compte", - "disconnected": "Non connecté", - "connectedAs": "Connecté en tant que {{username}}", - "login": "Se connecter avec Discord", - "logout": "Se déconnecter", - "subscription": "Abonnement", - "loading": "Connexion en cours...", - "cancel": "Annuler", - "unavailable": "Authentification non disponible pour cet environnement.", - "unavailableBtn": "Indisponible" + "game": { + "title": "Jeu", + "play": "Jouer", + "playDisabledMaintenance": "Le serveur est en maintenance — le lancement du jeu est désactivé.", + "playDisabledUpdate": "Une mise à jour du jeu est disponible — installez-la avant de jouer.", + "playDisabledAuth": "Connectez-vous pour lancer le jeu.", + "playDisabledAuthLoading": "Vérification de la session…", + "playDisabledRunning": "Le jeu est déjà lancé.", + "notInstalled": "Jeu non installé", + "players": "{{count}} joueurs en ligne", + "status": { + "online": "Opérationnel", + "degraded": "Dégradé", + "offline": "Hors ligne", + "maintenance": "En maintenance", + "unknown": "Inconnu", + "unavailable": "Non disponible" }, - "updateAlert": { - "launcher": { - "title": "Mise à jour du launcher disponible", - "discordHint": "La nouvelle version est disponible ici :", - "discordLink": "Discord" + "viewStatus": "Voir le statut" + }, + "files": { + "title": "Fichiers du jeu", + "version": "Version : {{version}}", + "releaseDate": "Release : {{date}}", + "notInstalled": "Jeu non installé", + "install": "Installer", + "update": "Mettre à jour", + "verify": "Vérifier les fichiers", + "clearCache": "Vider le cache", + "clearCacheToastOk": "Cache vidé.", + "clearCacheToastPartial": "Cache partiellement vidé (certains dossiers n'ont pas pu être supprimés).", + "clearCacheToastError": "Impossible de vider le cache.", + "changelog": "Voir le changelog", + "changelogModalTitle": "Changelog — v{{version}}", + "changelogLoading": "Chargement du changelog…", + "changelogMissing": "Aucun fichier CHANGELOG.md n'a été trouvé dans l'installation (vérifie qu'il est bien inclus dans l'archive du jeu).", + "installing": "Installation en cours...", + "installPath": "Répertoire d'installation", + "installPathPlaceholder": "/home/utilisateur/jeux/dyingstar", + "browse": "Parcourir", + "unavailable": "Installation non disponible pour cet environnement.", + "unavailableBtn": "Indisponible" + }, + "social": { + "title": "Social", + "noFriends": "Aucun ami", + "seeAll": "Voir tout", + "notifications": "{{count}} notifications" + }, + "socialPage": { + "sidebarTitle": "Social", + "sidebarSubtitle": "Amis, organisations, demandes.", + "friends": "Amis", + "organizations": "Organisations", + "requests": "Demandes", + "addFriend": "Ajouter un ami", + "createOrga": "Créer une orga", + "createOrgaLong": "Créer une organisation", + "joinOrga": "Rejoindre", + "accept": "Accepter", + "decline": "Refuser", + "tabs": { + "friends": { + "label": "Amis", + "subtitle": "Gère ta liste d'amis et ton statut." }, - "game": { - "title": "Nouvelle version du jeu disponible" + "organizations": { + "label": "Organisations", + "subtitle": "Organisations, membres et invitations." }, - "releaseOn": "Sortie le {{date}}" + "requests": { + "label": "Demandes", + "subtitle": "Demandes d'amis en attente." + } }, - "game": { - "title": "Jeu", - "play": "Jouer", - "playDisabledMaintenance": "Le serveur est en maintenance — le lancement du jeu est désactivé.", - "playDisabledUpdate": "Une mise à jour du jeu est disponible — installez-la avant de jouer.", - "playDisabledAuth": "Connectez-vous pour lancer le jeu.", - "playDisabledAuthLoading": "Vérification de la session…", - "playDisabledRunning": "Le jeu est déjà lancé.", - "notInstalled": "Jeu non installé", - "players": "{{count}} joueurs en ligne", - "status": { - "online": "Opérationnel", - "degraded": "Dégradé", - "offline": "Hors ligne", - "maintenance": "En maintenance", - "unknown": "Inconnu", - "unavailable": "Non disponible" - }, - "viewStatus": "Voir le statut" + "addFriendForm": { + "title": "Ajouter un ami", + "usernameLabel": "Pseudo exact", + "usernamePlaceholder": "Entrez un pseudo...", + "success": "Demande envoyée !" + }, + "createOrgaForm": { + "title": "Créer une organisation", + "nameLabel": "Nom de l'organisation", + "namePlaceholder": "Entrez un nom unique...", + "success": "Organisation créée !" }, - "files": { - "title": "Fichiers du jeu", - "version": "Version : {{version}}", - "releaseDate": "Release : {{date}}", - "notInstalled": "Jeu non installé", - "install": "Installer", - "update": "Mettre à jour", - "verify": "Vérifier les fichiers", - "clearCache": "Vider le cache", - "clearCacheToastOk": "Cache vidé.", - "clearCacheToastPartial": "Cache partiellement vidé (certains dossiers n’ont pas pu être supprimés).", - "clearCacheToastError": "Impossible de vider le cache.", - "changelog": "Voir le changelog", - "changelogModalTitle": "Changelog — v{{version}}", - "changelogLoading": "Chargement du changelog…", - "changelogMissing": "Aucun fichier CHANGELOG.md n’a été trouvé dans l’installation (vérifie qu’il est bien inclus dans l’archive du jeu).", - "installing": "Installation en cours...", - "installPath": "Répertoire d'installation", - "browse": "Parcourir", - "unavailable": "Installation non disponible pour cet environnement.", - "unavailableBtn": "Indisponible" + "pendingRequests_one": "{{count}} demande en attente", + "pendingRequests_other": "{{count}} demandes en attente", + "emptyFriends": "Aucun ami pour le moment.", + "emptyOrganizations": "Aucune organisation.", + "emptyRequests": "Aucune demande en attente.", + "orga": { + "members_one": "{{count}} membre", + "members_other": "{{count}} membres", + "memberBadge": "Membre", + "joinAria": "Rejoindre l'organisation {{name}}" }, - "social": { - "title": "Social", - "noFriends": "Aucun ami", - "seeAll": "Voir tout", - "notifications": "{{count}} notifications" + "request": { + "acceptAria": "Accepter la demande de {{name}}", + "declineAria": "Refuser la demande de {{name}}" }, - "socialPage": { - "friends": "Amis", - "organizations": "Organisations", - "requests": "Requêtes", - "addFriend": "Ajouter un ami", - "createOrga": "Créer une organisation", - "joinOrga": "Rejoindre", - "accept": "Accepter", - "decline": "Refuser" + "errors": { + "friendRequestFailed": "Utilisateur introuvable ou demande déjà envoyée.", + "orgaNameTaken": "Ce nom d'organisation est déjà pris.", + "joinOrgaFailed": "Erreur lors de la tentative." } } - } \ No newline at end of file + } +} diff --git a/src/renderer/i18n/index.ts b/src/renderer/i18n/index.ts index 6f3cec9..e42865a 100644 --- a/src/renderer/i18n/index.ts +++ b/src/renderer/i18n/index.ts @@ -1,21 +1,39 @@ -import i18n from "i18next" -import { initReactI18next } from "react-i18next" +/** i18next instance: FR/EN translations for the renderer. */ +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' -import en from "../i18n/en.json" -import fr from "../i18n/fr.json" +import en from '../i18n/en.json' +import fr from '../i18n/fr.json' -i18n - .use(initReactI18next) - .init({ - resources: { - en: { translation: en }, - fr: { translation: fr } - }, - lng: "en", // default language - fallbackLng: "fr", // fallback language - interpolation: { - escapeValue: false - } - }) +const STORAGE_KEY = 'ds-language' -export default i18n \ No newline at end of file +function detectLanguage(): 'en' | 'fr' { + if (typeof localStorage !== 'undefined') { + const stored = localStorage.getItem(STORAGE_KEY) + if (stored === 'en' || stored === 'fr') return stored + } + if (typeof navigator !== 'undefined' && navigator.language.toLowerCase().startsWith('fr')) { + return 'fr' + } + return 'en' +} + +void i18n.use(initReactI18next).init({ + resources: { + en: { translation: en }, + fr: { translation: fr } + }, + lng: detectLanguage(), + fallbackLng: 'en', + interpolation: { + escapeValue: false + } +}) + +i18n.on('languageChanged', (lng) => { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(STORAGE_KEY, lng) + } +}) + +export default i18n diff --git a/src/renderer/lib/env.ts b/src/renderer/lib/env.ts new file mode 100644 index 0000000..a4e88b4 --- /dev/null +++ b/src/renderer/lib/env.ts @@ -0,0 +1,51 @@ +import type { Env } from '@shared/types/env' + +/** Returns a trimmed env string or empty when unset. */ +export function envString(value: string | undefined): string { + return (value ?? '').trim() +} + +/** Status page URL for the given game environment. */ +export function statusPageUrlForEnv(env: Env): string { + const raw = + env === 'universe' + ? import.meta.env.VITE_STATUS_PAGE_UNIVERSE + : import.meta.env.VITE_STATUS_PAGE_TESTING + return envString(raw) +} + +/** Server status poll interval in minutes (clamped 1 … 1440). */ +export function pollIntervalMinutes(): number { + const raw = import.meta.env.VITE_SERVER_STATUS_POLL_MINUTES ?? '5' + let n = Number.parseFloat(String(raw).trim()) + if (!Number.isFinite(n) || n < 1) n = 5 + if (n > 24 * 60) n = 24 * 60 + return n +} + +/** External navigation link URLs for the navbar. */ +export function navUrls(): { + website: string + discord: string + wiki: string + donate: string +} { + return { + website: envString(import.meta.env.VITE_NAV_WEBSITE_URL), + discord: envString(import.meta.env.VITE_NAV_DISCORD_URL), + wiki: envString(import.meta.env.VITE_NAV_WIKI_URL), + donate: envString(import.meta.env.VITE_NAV_DONATE_URL) + } +} + +/** Toast notification duration in milliseconds. */ +export function toastDurationMs(): number { + const n = Number(import.meta.env.VITE_UI_TOAST_DURATION_MS ?? '3800') + return Number.isFinite(n) && n > 0 ? n : 3800 +} + +/** Auto-close delay for the social side panel in milliseconds. */ +export function socialPanelAutoCloseMs(): number { + const n = Number(import.meta.env.VITE_UI_SOCIAL_PANEL_CLOSE_MS ?? '1200') + return Number.isFinite(n) && n > 0 ? n : 1200 +} diff --git a/src/renderer/lib/formatInstallProgress.ts b/src/renderer/lib/formatInstallProgress.ts new file mode 100644 index 0000000..25f32cc --- /dev/null +++ b/src/renderer/lib/formatInstallProgress.ts @@ -0,0 +1,44 @@ +import type { TFunction } from 'i18next' +import type { InstallProgressLabel } from '@shared/types/installProgress' + +function formatBytes(bytes: number): string { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + return `${(bytes / (1024 * 1024)).toFixed(1)} MB` +} + +/** Maps a structured install progress event to a localized label. */ +export function formatInstallProgress(t: TFunction, label: InstallProgressLabel | null): string { + if (!label) return '' + + switch (label.key) { + case 'connecting': + return t('installProgress.connecting') + case 'checkingUpdates': + return t('installProgress.checkingUpdates') + case 'downloading': + return t('installProgress.downloading', { + downloaded: formatBytes(label.downloaded), + total: formatBytes(label.total) + }) + case 'downloadingIndeterminate': + return t('installProgress.downloadingIndeterminate', { + downloaded: formatBytes(label.downloaded) + }) + case 'preparing': + return t('installProgress.preparing') + case 'extracting': + return t('installProgress.extracting', { + current: label.current, + total: label.total + }) + case 'cleaning': + return t('installProgress.cleaning') + case 'completeInstall': + return t('installProgress.completeInstall', { version: label.version }) + case 'completeUpdate': + return t('installProgress.completeUpdate', { version: label.version }) + default: + return '' + } +} diff --git a/src/renderer/lib/formatReleaseDate.ts b/src/renderer/lib/formatReleaseDate.ts index 052c269..ee821fd 100644 --- a/src/renderer/lib/formatReleaseDate.ts +++ b/src/renderer/lib/formatReleaseDate.ts @@ -1,5 +1,6 @@ /** - * Affiche une date de release en jj/MM/aaaa (locale FR attendue dans l’UI). + * Formats a release date for display as DD/MM/YYYY (French UI locale). + * Accepts ISO dates, YYYYMMDDHHMMSS build ids, and parseable date strings. */ export function formatReleaseDateDisplay(raw: string | null | undefined): string { if (raw == null || raw === '') return '—' diff --git a/src/renderer/lib/isGameUpdateAvailable.ts b/src/renderer/lib/isGameUpdateAvailable.ts index 7d57e9c..c66f6af 100644 --- a/src/renderer/lib/isGameUpdateAvailable.ts +++ b/src/renderer/lib/isGameUpdateAvailable.ts @@ -1,4 +1,6 @@ -/** True si une version distante du jeu est strictement plus récente que la locale. */ +/** + * Returns true when a remote game version is strictly newer than the locally installed one. + */ export function isGameUpdateAvailable( installed: boolean, localVersion: string | null, @@ -8,6 +10,9 @@ export function isGameUpdateAvailable( installed && latestRemoteVersion !== null && localVersion !== null && - latestRemoteVersion.localeCompare(localVersion, undefined, { numeric: true, sensitivity: 'base' }) > 0 + latestRemoteVersion.localeCompare(localVersion, undefined, { + numeric: true, + sensitivity: 'base' + }) > 0 ) } diff --git a/src/renderer/lib/socialErrors.ts b/src/renderer/lib/socialErrors.ts new file mode 100644 index 0000000..d0ac45d --- /dev/null +++ b/src/renderer/lib/socialErrors.ts @@ -0,0 +1,29 @@ +/** Error codes thrown by the social store for i18n lookup in the UI. */ +export const SOCIAL_ERROR = { + ORGA_NAME_TAKEN: 'orgaNameTaken', + FRIEND_REQUEST_FAILED: 'friendRequestFailed' +} as const + +export type SocialErrorCode = (typeof SOCIAL_ERROR)[keyof typeof SOCIAL_ERROR] + +export class SocialStoreError extends Error { + constructor(public readonly code: SocialErrorCode) { + super(code) + this.name = 'SocialStoreError' + } +} + +/** Maps a social store error to a translated message, or uses the fallback i18n key. */ +export function getSocialErrorMessage( + err: unknown, + translate: (key: string) => string, + fallbackKey = 'universe.socialPage.errors.joinOrgaFailed' +): string { + if (err instanceof SocialStoreError) { + return translate(`universe.socialPage.errors.${err.code}`) + } + if (err instanceof Error && err.message) { + return err.message + } + return translate(fallbackKey) +} diff --git a/src/renderer/stores/account.ts b/src/renderer/stores/account.ts index b94e12f..6bc5bb6 100644 --- a/src/renderer/stores/account.ts +++ b/src/renderer/stores/account.ts @@ -1,79 +1,75 @@ import { create } from 'zustand' import { useEnvStore, type Env } from './env' -// ─── Types ──────────────────────────────────────────────────────────────────── - type AccountStatus = 'connected' | 'disconnected' | 'loading' type EnvAccountData = { - status: AccountStatus + status: AccountStatus username?: string - email?: string + email?: string } type AccountState = { data: Record - - login: () => Promise - logout: () => Promise + /** Opens OAuth login in the browser for the active environment. */ + login: () => Promise + /** Logs out and clears local session for the active environment. */ + logout: () => Promise + /** Cancels an in-progress login attempt (resets loading state). */ cancelLogin: () => void } -// ─── Valeurs par défaut ─────────────────────────────────────────────────────── - const defaultEnvData: EnvAccountData = { - status: 'disconnected', - username: undefined, - email: undefined + status: 'disconnected', + username: undefined, + email: undefined } const defaultData: Record = { - 'universe': { ...defaultEnvData }, + universe: { ...defaultEnvData }, 'universe-testing': { ...defaultEnvData } } -// ─── Helper ─────────────────────────────────────────────────────────────────── - type SetFn = (fn: (s: AccountState) => Partial) => void +/** Merges partial account fields for one environment into the store. */ function patchEnv(set: SetFn, env: Env, patch: Partial): void { set((s) => ({ data: { ...s.data, [env]: { ...s.data[env], ...patch } } })) } -// ─── Store ──────────────────────────────────────────────────────────────────── - +/** + * Per-environment Discord/Keycloak auth state and login actions. + */ export const useAccountStore = create((set) => { - - // Écoute les changements d'état auth envoyés depuis le main process. - // Le payload inclut maintenant l'env pour mettre à jour le bon slot. + // Main process pushes final auth result after OAuth callback window.api.onAuthStateChanged((data) => { if (data.status === 'connected') { patchEnv(set, data.env, { - status: 'connected', + status: 'connected', username: data.user.username, - email: data.user.email + email: data.user.email }) } else { console.error('[AccountStore] Auth error:', data.error) patchEnv(set, data.env, { - status: 'disconnected', + status: 'disconnected', username: undefined, - email: undefined + email: undefined }) } }) - // Restauration de session au démarrage — pour chaque env séparément + // Restore persisted sessions on startup (one request per env) const envs: Env[] = ['universe', 'universe-testing'] envs.forEach((env) => { window.api.authLoadUser(env).then((user) => { if (user) { patchEnv(set, env, { - status: 'connected', + status: 'connected', username: user.username, - email: user.email + email: user.email }) } }) @@ -87,7 +83,7 @@ export const useAccountStore = create((set) => { patchEnv(set, env, { status: 'loading' }) try { await window.api.authLogin(env) - // La mise à jour finale arrive via onAuthStateChanged + // Connected state is applied via onAuthStateChanged } catch { patchEnv(set, env, { status: 'disconnected' }) } @@ -104,4 +100,4 @@ export const useAccountStore = create((set) => { patchEnv(set, env, { status: 'disconnected' }) } } -}) \ No newline at end of file +}) diff --git a/src/renderer/stores/availability.ts b/src/renderer/stores/availability.ts index 848b97a..2b99cd9 100644 --- a/src/renderer/stores/availability.ts +++ b/src/renderer/stores/availability.ts @@ -1,19 +1,20 @@ -// Disponibilité runtime de chaque env — remplace envCapabilities.ts statique. -// Déterminée au démarrage en pingant ${API_BASE}/version pour chaque env. - import { create } from 'zustand' import type { Env } from './env' type AvailabilityState = { + /** Whether each env API responded successfully to `/version` at startup. */ available: Record - checked: boolean - + checked: boolean + /** Pings remote APIs and updates availability flags. */ checkAvailability: () => Promise } +/** + * Runtime availability of prod vs testing API bases (determined at app startup). + */ export const useAvailabilityStore = create((set) => ({ available: { - 'universe': false, + universe: false, 'universe-testing': false }, checked: false, @@ -23,7 +24,8 @@ export const useAvailabilityStore = create((set) => ({ const result = await window.api.checkEnvAvailability() set({ available: result, checked: true }) } catch { - set({ checked: true }) // en cas d'erreur, tout reste false + // On failure, leave all envs as unavailable + set({ checked: true }) } } -})) \ No newline at end of file +})) diff --git a/src/renderer/stores/env.ts b/src/renderer/stores/env.ts index ccaae22..1e0ee45 100644 --- a/src/renderer/stores/env.ts +++ b/src/renderer/stores/env.ts @@ -5,10 +5,14 @@ export type { Env } from '@shared/types/env' interface EnvState { activeEnv: Env + /** Switches the active universe (prod vs testing) for all env-scoped stores. */ setEnv: (env: Env) => void } +/** + * Tracks which game environment (universe / universe-testing) the UI operates on. + */ export const useEnvStore = create((set) => ({ activeEnv: 'universe', setEnv: (env) => set({ activeEnv: env }) -})) \ No newline at end of file +})) diff --git a/src/renderer/stores/files.ts b/src/renderer/stores/files.ts index a1b0c47..5261154 100644 --- a/src/renderer/stores/files.ts +++ b/src/renderer/stores/files.ts @@ -1,40 +1,37 @@ import { create } from 'zustand' import { persist } from 'zustand/middleware' +import type { InstallProgressLabel } from '@shared/types/installProgress' import { useEnvStore, type Env } from './env' import { useVersionStore } from './version' -// ─── Types ──────────────────────────────────────────────────────────────────── - export type EnvFilesData = { - // Persistés + /** Persisted: game payload is installed under installPath/DyingStar */ installed: boolean version: string | null releaseDate: string | null needsUpdate: boolean installPath: string - // Transitoires + /** Transient: install/update in progress */ installing: boolean progress: number - progressLabel: string + progressEvent: InstallProgressLabel | null } -/** Résultat du vidage cache Godot (shader_cache / chunk_cache). */ +/** Outcome of clearing Godot shader/chunk caches. */ export type ClearGodotCacheOutcome = 'success' | 'partial' | 'error' type FilesState = { data: Record - setInstallPath: (path: string) => void selectDirectory: () => Promise install: () => Promise update: () => Promise verify: () => Promise clearCache: () => Promise + /** Aligns local version with remote API after rehydrate or manual refresh. */ syncInstalledVersions: () => Promise } -// ─── Valeurs par défaut ─────────────────────────────────────────────────────── - const defaultEnvData: EnvFilesData = { installed: false, version: null, @@ -43,26 +40,27 @@ const defaultEnvData: EnvFilesData = { installPath: '', installing: false, progress: 0, - progressLabel: '' + progressEvent: null } const defaultData: Record = { - 'universe': { ...defaultEnvData }, + universe: { ...defaultEnvData }, 'universe-testing': { ...defaultEnvData } } -// ─── Helper ─────────────────────────────────────────────────────────────────── - type SetFn = (fn: (s: FilesState) => Partial) => void +/** Merges partial file/install fields for one environment. */ function patchEnv(set: SetFn, env: Env, patch: Partial): void { set((s) => ({ data: { ...s.data, [env]: { ...s.data[env], ...patch } } })) } -// ─── Store ──────────────────────────────────────────────────────────────────── - +/** + * Per-environment install path, download progress, and install/update actions. + * Install metadata is persisted to disk via zustand/persist. + */ export const useFilesStore = create()( persist( (set, get) => ({ @@ -85,18 +83,17 @@ export const useFilesStore = create()( const env = useEnvStore.getState().activeEnv const { installPath } = get().data[env] if (!installPath) { - console.warn('[FilesStore] Aucun répertoire d\'installation défini.') + console.warn('[FilesStore] No install directory set.') return } - patchEnv(set, env, { installing: true, progress: 0, progressLabel: 'Connexion au serveur...' }) + patchEnv(set, env, { installing: true, progress: 0, progressEvent: { key: 'connecting' } }) window.api.onInstallProgress((progress, label) => { - patchEnv(set, env, { progress, progressLabel: label }) + patchEnv(set, env, { progress, progressEvent: label }) }) try { - // installGame retourne maintenant { version, releaseDate } depuis version.json const { version, releaseDate } = await window.api.installGame(env, installPath) patchEnv(set, env, { @@ -106,12 +103,12 @@ export const useFilesStore = create()( installing: false, needsUpdate: false, progress: 100, - progressLabel: `Installation terminée — v${version}` + progressEvent: { key: 'completeInstall', version } }) void useVersionStore.getState().checkVersions() } catch (err) { - console.error('[FilesStore] Échec installation :', err) - patchEnv(set, env, { installing: false, progress: 0, progressLabel: '' }) + console.error('[FilesStore] Install failed:', err) + patchEnv(set, env, { installing: false, progress: 0, progressEvent: null }) } }, @@ -120,10 +117,14 @@ export const useFilesStore = create()( const { installPath } = get().data[env] if (!installPath) return - patchEnv(set, env, { installing: true, progress: 0, progressLabel: 'Recherche des mises à jour...' }) + patchEnv(set, env, { + installing: true, + progress: 0, + progressEvent: { key: 'checkingUpdates' } + }) window.api.onInstallProgress((progress, label) => { - patchEnv(set, env, { progress, progressLabel: label }) + patchEnv(set, env, { progress, progressEvent: label }) }) try { @@ -132,7 +133,7 @@ export const useFilesStore = create()( try { await window.api.clearGodotGameCache() } catch (cacheErr) { - console.warn('[FilesStore] Cache Godot après mise à jour :', cacheErr) + console.warn('[FilesStore] Godot cache clear after update:', cacheErr) } patchEnv(set, env, { @@ -141,32 +142,35 @@ export const useFilesStore = create()( installing: false, needsUpdate: false, progress: 100, - progressLabel: `Mise à jour terminée — v${version}` + progressEvent: { key: 'completeUpdate', version } }) void useVersionStore.getState().checkVersions() } catch (err) { - console.error('[FilesStore] Échec mise à jour :', err) - patchEnv(set, env, { installing: false, progress: 0, progressLabel: '' }) + console.error('[FilesStore] Update failed:', err) + patchEnv(set, env, { installing: false, progress: 0, progressEvent: null }) } }, verify: async () => { const env = useEnvStore.getState().activeEnv const { installPath } = get().data[env] - console.log('[FilesStore] Vérification dans :', installPath, '(env:', env, ')') + console.log('[FilesStore] Verify install path:', installPath, 'env:', env) }, clearCache: async (): Promise => { try { const result = await window.api.clearGodotGameCache() if (result.errors.length > 0) { - console.warn('[FilesStore] Cache Godot — erreurs :', result.errors) + console.warn('[FilesStore] Godot cache — errors:', result.errors) return 'partial' } - console.log('[FilesStore] Cache Godot vidé', { root: result.root, removed: result.removed.length }) + console.log('[FilesStore] Godot cache cleared', { + root: result.root, + removed: result.removed.length + }) return 'success' } catch (err) { - console.error('[FilesStore] Échec vidage cache Godot :', err) + console.error('[FilesStore] Godot cache clear failed:', err) return 'error' } }, @@ -185,7 +189,7 @@ export const useFilesStore = create()( releaseDate: resolved.releaseDate }) } catch (err) { - console.warn('[FilesStore] Sync version installée :', env, err) + console.warn('[FilesStore] Sync installed version:', env, err) } }) ) @@ -193,37 +197,40 @@ export const useFilesStore = create()( }), { name: 'dyingstar-files', + /** Persists only install metadata, not transient progress flags. */ partialize: (state) => ({ data: { - 'universe': { - installed: state.data['universe'].installed, - version: state.data['universe'].version, - releaseDate: state.data['universe'].releaseDate, - needsUpdate: state.data['universe'].needsUpdate, - installPath: state.data['universe'].installPath + universe: { + installed: state.data.universe.installed, + version: state.data.universe.version, + releaseDate: state.data.universe.releaseDate, + needsUpdate: state.data.universe.needsUpdate, + installPath: state.data.universe.installPath }, 'universe-testing': { - installed: state.data['universe-testing'].installed, - version: state.data['universe-testing'].version, + installed: state.data['universe-testing'].installed, + version: state.data['universe-testing'].version, releaseDate: state.data['universe-testing'].releaseDate, needsUpdate: state.data['universe-testing'].needsUpdate, installPath: state.data['universe-testing'].installPath } } }), + /** Restores persisted slice and merges with default transient fields. */ merge: (persisted: unknown, current) => { const p = persisted as Partial return { ...current, data: { - 'universe': { ...defaultEnvData, ...(p.data?.['universe'] ?? {}) }, + universe: { ...defaultEnvData, ...(p.data?.universe ?? {}) }, 'universe-testing': { ...defaultEnvData, ...(p.data?.['universe-testing'] ?? {}) } } } }, + /** After disk rehydrate, refresh versions from API/manifest. */ onRehydrateStorage: () => (state) => { if (state) void state.syncInstalledVersions() } } ) -) \ No newline at end of file +) diff --git a/src/renderer/stores/game.ts b/src/renderer/stores/game.ts index f61523b..9232435 100644 --- a/src/renderer/stores/game.ts +++ b/src/renderer/stores/game.ts @@ -3,28 +3,30 @@ import { useEnvStore, type Env } from './env' import { useFilesStore } from './files' import type { ServerStatusValue } from '@shared/types/game' -// ─── Types ──────────────────────────────────────────────────────────────────── - export type { ServerStatusValue } type EnvGameData = { - status: ServerStatusValue + status: ServerStatusValue players: number } type GameState = { data: Record + /** True while a game process launched by the launcher is running. */ gameRunning: boolean - + /** Refreshes server status and player count for the active environment. */ fetchServerStatus: () => Promise + /** Syncs running state from the main process (e.g. after failed launch). */ syncGameRunning: () => Promise + /** Launches the game executable with a fresh auth token. */ play: () => Promise } const defaultEnvData: EnvGameData = { status: 'unknown', players: 0 } -// ─── Store ──────────────────────────────────────────────────────────────────── - +/** + * Server status, player count, and game launch state per environment. + */ export const useGameStore = create((set, get) => { window.api.onGameRunningChanged((running) => { set({ gameRunning: running }) @@ -36,7 +38,7 @@ export const useGameStore = create((set, get) => { return { data: { - 'universe': { ...defaultEnvData }, + universe: { ...defaultEnvData }, 'universe-testing': { ...defaultEnvData } }, gameRunning: false, @@ -71,7 +73,7 @@ export const useGameStore = create((set, get) => { const { installPath } = useFilesStore.getState().data[env] if (!installPath) { - console.warn('[GameStore] Aucun répertoire d\'installation pour env :', env) + console.warn('[GameStore] No install path for env:', env) return } @@ -79,7 +81,7 @@ export const useGameStore = create((set, get) => { await window.api.launchGame(env, installPath) set({ gameRunning: true }) } catch (err) { - console.error('[GameStore] Échec du lancement :', err) + console.error('[GameStore] Launch failed:', err) await get().syncGameRunning() } } diff --git a/src/renderer/stores/lore.ts b/src/renderer/stores/lore.ts index 351be59..1e8d785 100644 --- a/src/renderer/stores/lore.ts +++ b/src/renderer/stores/lore.ts @@ -2,24 +2,26 @@ import { create } from 'zustand' type Article = { id: string - title: string file: string } type LoreState = { articles: Article[] current?: Article - + /** Selects a lore article by id for display in LoreArticle. */ select: (id: string) => void } +/** + * Lore sidebar catalog and currently selected markdown article. + * Article titles are resolved via i18n (`lore.articles.`). + */ export const useLoreStore = create((set, get) => ({ - articles: [{ id: 'origins', title: 'Origines', file: 'origins.md' }], - + articles: [{ id: 'origins', file: 'origins.md' }], current: undefined, select: (id) => { const article = get().articles.find((a) => a.id === id) set({ current: article }) } -})) \ No newline at end of file +})) diff --git a/src/renderer/stores/navigation.ts b/src/renderer/stores/navigation.ts index c59a93d..8fa1601 100644 --- a/src/renderer/stores/navigation.ts +++ b/src/renderer/stores/navigation.ts @@ -1,13 +1,18 @@ import { create } from 'zustand' +/** Main shell views: universe panels, social page, or lore page. */ export type View = 'universe' | 'universe-testing' | 'social' | 'lore' interface NavigationState { currentView: View + /** Switches the active shell view. */ navigate: (view: View) => void } +/** + * Controls which top-level view is rendered inside the shell (below the navbar). + */ export const useNavigationStore = create((set) => ({ currentView: 'universe', navigate: (view) => set({ currentView: view }) -})) \ No newline at end of file +})) diff --git a/src/renderer/stores/social.ts b/src/renderer/stores/social.ts index b209258..8517013 100644 --- a/src/renderer/stores/social.ts +++ b/src/renderer/stores/social.ts @@ -1,7 +1,6 @@ import { create } from 'zustand' import { useEnvStore, type Env } from './env' - -// ─── Types ──────────────────────────────────────────────────────────────────── +import { SOCIAL_ERROR, SocialStoreError } from '@lib/socialErrors' export type FriendStatus = 'online' | 'offline' | 'ingame' @@ -17,7 +16,8 @@ export type Orga = { id: string name: string members: number - isMember: boolean // ← l'API indique si l'utilisateur est déjà membre + /** Whether the current user is already a member (from API). */ + isMember: boolean } export type FriendRequest = { @@ -34,24 +34,16 @@ type EnvSocialData = { type SocialState = { data: Record - + /** Loads friends, orgs, and requests for the active env (mock until API wired). */ fetchAll: () => Promise - - // Amis addFriend: (username: string) => Promise removeFriend: (id: string) => void - - // Demandes acceptRequest: (id: string) => Promise declineRequest: (id: string) => Promise - - // Organisations joinOrga: (id: string) => Promise createOrga: (name: string) => Promise } -// ─── Valeurs par défaut ─────────────────────────────────────────────────────── - const defaultEnvData: EnvSocialData = { friends: [], orgas: [], @@ -60,53 +52,46 @@ const defaultEnvData: EnvSocialData = { } const defaultData: Record = { - 'universe': { ...defaultEnvData }, + universe: { ...defaultEnvData }, 'universe-testing': { ...defaultEnvData } } -// ─── Helper ─────────────────────────────────────────────────────────────────── - type SetFn = (fn: (s: SocialState) => Partial) => void +/** Merges partial social data for one environment. */ function patchEnv(set: SetFn, env: Env, patch: Partial): void { set((s) => ({ data: { ...s.data, [env]: { ...s.data[env], ...patch } } })) } -// ─── Store ──────────────────────────────────────────────────────────────────── - +/** + * Social graph state per environment (friends, orgs, requests). + * Currently backed by mock data; replace with API calls when available. + */ export const useSocialStore = create((set, get) => ({ data: defaultData, - // ── Chargement ───────────────────────────────────────────────────────────── - fetchAll: async () => { const env = useEnvStore.getState().activeEnv - // TODO: vrais appels API selon l'env - // const [friends, orgas, requests] = await Promise.all([ - // fetch(`${API_BASE[env]}/friends`).then(r => r.json()), - // fetch(`${API_BASE[env]}/organizations`).then(r => r.json()), // isMember inclus - // fetch(`${API_BASE[env]}/friend-requests`).then(r => r.json()), - // ]) - + // TODO: replace with real API calls per env await new Promise((r) => setTimeout(r, 500)) const mockData: Record = { - 'universe': { + universe: { friends: [ - { id: '1', name: 'Aurel', status: 'online' }, - { id: '2', name: 'Kira', status: 'ingame', game: 'Universe' }, - { id: '3', name: 'Noah', status: 'offline' } + { id: '1', name: 'Aurel', status: 'online' }, + { id: '2', name: 'Kira', status: 'ingame', game: 'Universe' }, + { id: '3', name: 'Noah', status: 'offline' } ], orgas: [ - { id: '1', name: 'Nova Corp', members: 12, isMember: true }, - { id: '2', name: 'Galaxy Team', members: 5, isMember: false }, - { id: '3', name: 'Void Squad', members: 8, isMember: false } + { id: '1', name: 'Nova Corp', members: 12, isMember: true }, + { id: '2', name: 'Galaxy Team', members: 5, isMember: false }, + { id: '3', name: 'Void Squad', members: 8, isMember: false } ], requests: [ - { id: '1', name: 'Zed' }, + { id: '1', name: 'Zed' }, { id: '2', name: 'Mira' } ], loaded: true @@ -116,9 +101,7 @@ export const useSocialStore = create((set, get) => ({ { id: '1', name: 'TestUser1', status: 'online' }, { id: '2', name: 'TestUser2', status: 'ingame', game: 'Universe Testing' } ], - orgas: [ - { id: '1', name: 'Test Squad', members: 3, isMember: false } - ], + orgas: [{ id: '1', name: 'Test Squad', members: 3, isMember: false }], requests: [], loaded: true } @@ -127,43 +110,40 @@ export const useSocialStore = create((set, get) => ({ patchEnv(set, env, mockData[env]) }, - // ── Amis ─────────────────────────────────────────────────────────────────── - addFriend: async (username: string) => { const env = useEnvStore.getState().activeEnv const trimmed = username.trim() if (!trimmed) return - // TODO: appel API - // const res = await fetch(`${API_BASE[env]}/friend-requests`, { - // method: 'POST', body: JSON.stringify({ username: trimmed }) - // }) - // if (!res.ok) throw new Error('Utilisateur introuvable ou demande déjà envoyée.') + const { friends, requests } = get().data[env] + const nameTaken = + friends.some((f) => f.name.toLowerCase() === trimmed.toLowerCase()) || + requests.some((r) => r.name.toLowerCase() === trimmed.toLowerCase()) + // TODO: POST friend request API await new Promise((r) => setTimeout(r, 600)) - console.log(`[SocialStore] Demande envoyée à "${trimmed}" sur env: ${env}`) + + if (nameTaken) { + throw new SocialStoreError(SOCIAL_ERROR.FRIEND_REQUEST_FAILED) + } + + console.log(`[SocialStore] Friend request sent to "${trimmed}" on env:`, env) }, removeFriend: (id: string) => { const env = useEnvStore.getState().activeEnv const { friends } = get().data[env] - - // TODO: appel API DELETE /friends/:id - + // TODO: DELETE /friends/:id patchEnv(set, env, { friends: friends.filter((f) => f.id !== id) }) }, - // ── Demandes d'amis ──────────────────────────────────────────────────────── - acceptRequest: async (id: string) => { const env = useEnvStore.getState().activeEnv const { friends, requests } = get().data[env] const req = requests.find((r) => r.id === id) if (!req) return - // TODO: appel API POST /friend-requests/:id/accept - // await fetch(`${API_BASE[env]}/friend-requests/${id}/accept`, { method: 'POST' }) - + // TODO: POST /friend-requests/:id/accept await new Promise((r) => setTimeout(r, 400)) patchEnv(set, env, { @@ -176,30 +156,21 @@ export const useSocialStore = create((set, get) => ({ const env = useEnvStore.getState().activeEnv const { requests } = get().data[env] - // TODO: appel API POST /friend-requests/:id/decline - // await fetch(`${API_BASE[env]}/friend-requests/${id}/decline`, { method: 'POST' }) - + // TODO: POST /friend-requests/:id/decline await new Promise((r) => setTimeout(r, 300)) patchEnv(set, env, { requests: requests.filter((r) => r.id !== id) }) }, - // ── Organisations ────────────────────────────────────────────────────────── - joinOrga: async (id: string) => { const env = useEnvStore.getState().activeEnv const { orgas } = get().data[env] - // TODO: appel API POST /organizations/:id/join - // const res = await fetch(`${API_BASE[env]}/organizations/${id}/join`, { method: 'POST' }) - // if (!res.ok) throw new Error('Impossible de rejoindre cette organisation.') - + // TODO: POST /organizations/:id/join await new Promise((r) => setTimeout(r, 500)) patchEnv(set, env, { - orgas: orgas.map((o) => - o.id === id ? { ...o, isMember: true, members: o.members + 1 } : o - ) + orgas: orgas.map((o) => (o.id === id ? { ...o, isMember: true, members: o.members + 1 } : o)) }) }, @@ -208,18 +179,11 @@ export const useSocialStore = create((set, get) => ({ const trimmed = name.trim() if (!trimmed) return - // TODO: appel API POST /organizations - // const res = await fetch(`${API_BASE[env]}/organizations`, { - // method: 'POST', body: JSON.stringify({ name: trimmed }) - // }) - // const json = await res.json() - // if (json === false || !json.success) throw new Error('Ce nom d\'organisation est déjà pris.') - + // TODO: POST /organizations await new Promise((r) => setTimeout(r, 600)) - // Simulation : "DyingStar" est un nom déjà pris if (trimmed.toLowerCase() === 'dyingstar') { - throw new Error('Ce nom d\'organisation est déjà pris.') + throw new SocialStoreError(SOCIAL_ERROR.ORGA_NAME_TAKEN) } const { orgas } = get().data[env] @@ -232,4 +196,4 @@ export const useSocialStore = create((set, get) => ({ patchEnv(set, env, { orgas: [...orgas, newOrga] }) } -})) \ No newline at end of file +})) diff --git a/src/renderer/stores/version.ts b/src/renderer/stores/version.ts index 3b38058..32b5b2f 100644 --- a/src/renderer/stores/version.ts +++ b/src/renderer/stores/version.ts @@ -5,57 +5,53 @@ import type { GameVersionInfo } from '@shared/types/version' export type { GameVersionInfo } from '@shared/types/version' type VersionState = { - // Launcher - currentLauncherVersion: string - latestLauncherVersion: string | null + currentLauncherVersion: string + latestLauncherVersion: string | null latestLauncherReleaseDate: string | null - launcherUpdateAvailable: boolean - - // Jeu par env — version ET date de la release distante + launcherUpdateAvailable: boolean + /** Latest remote game version per environment (from API `/version`). */ latestGameVersions: Record - checking: boolean - checked: boolean - + checked: boolean + /** Fetches launcher (GitHub) and game (API) version info from the main process. */ checkVersions: () => Promise } -// ─── Valeurs par défaut ─────────────────────────────────────────────────────── - const defaultGameVersionInfo: GameVersionInfo = { version: null, releaseDate: null } -// ─── Store ──────────────────────────────────────────────────────────────────── - +/** + * Launcher and per-env game version state for update banners. + */ export const useVersionStore = create((set) => ({ - currentLauncherVersion: '', - latestLauncherVersion: null, + currentLauncherVersion: '', + latestLauncherVersion: null, latestLauncherReleaseDate: null, - launcherUpdateAvailable: false, + launcherUpdateAvailable: false, latestGameVersions: { - 'universe': { ...defaultGameVersionInfo }, + universe: { ...defaultGameVersionInfo }, 'universe-testing': { ...defaultGameVersionInfo } }, checking: false, - checked: false, + checked: false, checkVersions: async () => { set({ checking: true }) try { const result = await window.api.checkVersions() set({ - currentLauncherVersion: result.currentLauncherVersion, - latestLauncherVersion: result.latestLauncherVersion, + currentLauncherVersion: result.currentLauncherVersion, + latestLauncherVersion: result.latestLauncherVersion, latestLauncherReleaseDate: result.latestLauncherReleaseDate, - launcherUpdateAvailable: result.launcherUpdateAvailable, - latestGameVersions: result.latestGameVersions, + launcherUpdateAvailable: result.launcherUpdateAvailable, + latestGameVersions: result.latestGameVersions, checking: false, - checked: true + checked: true }) } catch (err) { - console.error('[VersionStore] Échec du check :', err) + console.error('[VersionStore] Version check failed:', err) set({ checking: false, checked: true }) } } -})) \ No newline at end of file +})) diff --git a/src/renderer/views/Lore.tsx b/src/renderer/views/Lore.tsx index b899713..e185204 100644 --- a/src/renderer/views/Lore.tsx +++ b/src/renderer/views/Lore.tsx @@ -1,11 +1,13 @@ +import type React from 'react' import LoreSidebar from '@components/ui/lore/LoreSidebar' import LoreArticle from '@components/ui/lore/LoreArticle' -export default function LorePage() { +/** Lore page: article sidebar and markdown content viewer. */ +export default function LorePage(): React.JSX.Element { return ( -
+
) -} \ No newline at end of file +} diff --git a/src/renderer/views/Social.tsx b/src/renderer/views/Social.tsx index 81b16da..8125798 100644 --- a/src/renderer/views/Social.tsx +++ b/src/renderer/views/Social.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useSocialStore } from '@stores/social' import { useEnvStore } from '@stores/env' import SocialSidebar from '@components/ui/social/SocialSidebar' @@ -7,10 +8,12 @@ import InputField from '@components/ui/primitives/InputField' import { FriendRow } from '@components/ui/social/FriendRow' import { OrgaRow } from '@components/ui/social/OrgaRow' import { RequestRow } from '@components/ui/social/RequestRow' +import { socialPanelAutoCloseMs } from '@lib/env' +import { getSocialErrorMessage } from '@lib/socialErrors' -// ─── Formulaire : ajouter un ami ────────────────────────────────────────────── - +/** Inline form to send a friend request by username. */ function AddFriendForm({ onClose }: { onClose: () => void }): React.JSX.Element { + const { t } = useTranslation() const { addFriend } = useSocialStore() const [username, setUsername] = useState('') const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle') @@ -24,45 +27,46 @@ function AddFriendForm({ onClose }: { onClose: () => void }): React.JSX.Element await addFriend(username) setStatus('success') setUsername('') - setTimeout(onClose, 1200) + setTimeout(onClose, socialPanelAutoCloseMs()) } catch (err) { setStatus('error') - setErrorMsg(err instanceof Error ? err.message : 'Utilisateur introuvable ou demande déjà envoyée.') + setErrorMsg(getSocialErrorMessage(err, t, 'universe.socialPage.errors.friendRequestFailed')) } } return ( -
+

- Ajouter un ami + {t('universe.socialPage.addFriendForm.title')}

{status === 'success' && ( -

Demande envoyée !

- )} - {status === 'error' && ( -

{errorMsg}

+

{t('universe.socialPage.addFriendForm.success')}

)} + {status === 'error' &&

{errorMsg}

}
) } -// ─── Formulaire : créer une organisation ────────────────────────────────────── - +/** Inline form to create a new organization. */ function CreateOrgaForm({ onClose }: { onClose: () => void }): React.JSX.Element { + const { t } = useTranslation() const { createOrga } = useSocialStore() const [name, setName] = useState('') const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle') @@ -76,64 +80,50 @@ function CreateOrgaForm({ onClose }: { onClose: () => void }): React.JSX.Element await createOrga(name) setStatus('success') setName('') - setTimeout(onClose, 1200) + setTimeout(onClose, socialPanelAutoCloseMs()) } catch (err) { setStatus('error') - setErrorMsg(err instanceof Error ? err.message : 'Ce nom d\'organisation est déjà pris.') + setErrorMsg(getSocialErrorMessage(err, t)) } } return ( -
+

- Créer une organisation + {t('universe.socialPage.createOrgaForm.title')}

{status === 'success' && ( -

Organisation créée !

- )} - {status === 'error' && ( -

{errorMsg}

+

+ {t('universe.socialPage.createOrgaForm.success')} +

)} + {status === 'error' &&

{errorMsg}

}
) } -// ─── Types ──────────────────────────────────────────────────────────────────── - type Tab = 'friends' | 'organizations' | 'requests' -const TAB_META: Record = { - friends: { - label: 'Amis', - subtitle: 'Gère ta liste d\'amis et ton statut.' - }, - organizations: { - label: 'Organisations', - subtitle: 'Organisations, membres et invitations.' - }, - requests: { - label: 'Demandes', - subtitle: 'Demandes d\'amis en attente.' - } -} - -// ─── Page ───────────────────────────────────────────────────────────────────── - +/** Full social page: friends, organizations, and friend requests. */ export default function SocialPage(): React.JSX.Element { + const { t } = useTranslation() const { activeEnv } = useEnvStore() const { data, fetchAll, acceptRequest, declineRequest, joinOrga } = useSocialStore() const { friends, orgas, requests } = data[activeEnv] @@ -143,8 +133,8 @@ export default function SocialPage(): React.JSX.Element { const [showCreateOrga, setShowCreateOrga] = useState(false) useEffect(() => { - fetchAll() - }, [activeEnv]) + void fetchAll() + }, [activeEnv, fetchAll]) const handleTabChange = (next: string): void => { setTab(next as Tab) @@ -152,47 +142,46 @@ export default function SocialPage(): React.JSX.Element { setShowCreateOrga(false) } - const meta = TAB_META[tab] + const tabLabel = t(`universe.socialPage.tabs.${tab}.label`) + const tabSubtitle = t(`universe.socialPage.tabs.${tab}.subtitle`) return ( -
- +
-
- - {/* Header */} +
-

- {meta.label} -

-

- {meta.subtitle} -

+

{tabLabel}

+

{tabSubtitle}

{tab === 'friends' && ( )} {tab === 'organizations' && ( )}
- {/* Formulaires contextuels */} {showAddFriend && tab === 'friends' && ( setShowAddFriend(false)} /> )} @@ -200,22 +189,19 @@ export default function SocialPage(): React.JSX.Element { setShowCreateOrga(false)} /> )} - {/* Compteur requêtes */} {tab === 'requests' && requests.length > 0 && (

- {requests.length} demande{requests.length > 1 ? 's' : ''} en attente + {t('universe.socialPage.pendingRequests', { count: requests.length })}

)} - {/* Contenu */} -
+
- {tab === 'friends' && ( <> {friends.length === 0 && (

- Aucun ami pour le moment. + {t('universe.socialPage.emptyFriends')}

)} {friends.map((f) => ( @@ -228,7 +214,7 @@ export default function SocialPage(): React.JSX.Element { <> {orgas.length === 0 && (

- Aucune organisation. + {t('universe.socialPage.emptyOrganizations')}

)} {orgas.map((o) => ( @@ -241,7 +227,7 @@ export default function SocialPage(): React.JSX.Element { <> {requests.length === 0 && (

- Aucune demande en attente. + {t('universe.socialPage.emptyRequests')}

)} {requests.map((r) => ( @@ -254,11 +240,9 @@ export default function SocialPage(): React.JSX.Element { ))} )} -
-
) -} \ No newline at end of file +} diff --git a/src/renderer/views/UniverseView.tsx b/src/renderer/views/UniverseView.tsx index cfec0b5..a7dd0f1 100644 --- a/src/renderer/views/UniverseView.tsx +++ b/src/renderer/views/UniverseView.tsx @@ -11,6 +11,7 @@ type UniverseViewProps = { showTestingBanner?: boolean } +/** Four-panel universe layout (account, social preview, game, files) with optional testing banner. */ export default function UniverseView({ showTestingBanner = false }: UniverseViewProps): React.JSX.Element { @@ -24,11 +25,15 @@ export default function UniverseView({ } = useVersionStore() return ( -
+
{showTestingBanner && (
-
)} -
+
diff --git a/src/shared/env.d.ts b/src/shared/env.d.ts index 8be8ca8..9ef08e4 100644 --- a/src/shared/env.d.ts +++ b/src/shared/env.d.ts @@ -1,4 +1,4 @@ -/** Variables d'environnement Vite (main + renderer). */ +/** Vite environment variables (main + renderer). */ interface ImportMetaEnv { readonly VITE_API_BASE_UNIVERSE: string readonly VITE_API_BASE_TESTING: string @@ -14,6 +14,8 @@ interface ImportMetaEnv { readonly VITE_LAUNCHER_RELEASE_DISCORD_URL: string readonly VITE_WINDOW_WIDTH: string readonly VITE_WINDOW_HEIGHT: string + readonly VITE_WINDOW_MAX_WIDTH: string + readonly VITE_WINDOW_MAX_HEIGHT: string readonly VITE_ENABLE_DEVTOOLS: string readonly VITE_ELECTRON_ENABLE_LOGGING: string readonly VITE_STATUS_COMPONENT_ID_UNIVERSE: string @@ -27,6 +29,22 @@ interface ImportMetaEnv { readonly VITE_NAV_DISCORD_URL: string readonly VITE_NAV_WIKI_URL: string readonly VITE_NAV_DONATE_URL: string + readonly VITE_HTTP_TIMEOUT_API_MS: string + readonly VITE_HTTP_TIMEOUT_VERSION_PING_MS: string + readonly VITE_HTTP_TIMEOUT_GITHUB_MS: string + readonly VITE_DOWNLOAD_TIMEOUT_MS: string + readonly VITE_GAME_PROCESS_POLL_MS: string + readonly VITE_GAME_INSTALL_SUBDIR: string + readonly VITE_GAME_EXE_WINDOWS: string + readonly VITE_GAME_EXE_LINUX: string + readonly VITE_GAME_EXE_DARWIN: string + readonly VITE_APP_PROTOCOL: string + readonly VITE_DOWNLOAD_USER_AGENT: string + readonly VITE_AUTH_CLIENT_ID: string + readonly VITE_AUTH_REALM: string + readonly VITE_AUTH_REDIRECT_URI: string + readonly VITE_UI_TOAST_DURATION_MS: string + readonly VITE_UI_SOCIAL_PANEL_CLOSE_MS: string } interface ImportMeta { diff --git a/src/shared/types/auth.ts b/src/shared/types/auth.ts index aa8f1a7..9781873 100644 --- a/src/shared/types/auth.ts +++ b/src/shared/types/auth.ts @@ -1,3 +1,4 @@ +/** Authenticated player profile decoded from the OAuth id_token. */ export interface UserInfo { sub: string username: string diff --git a/src/shared/types/env.ts b/src/shared/types/env.ts index bdab956..cfcf2ee 100644 --- a/src/shared/types/env.ts +++ b/src/shared/types/env.ts @@ -1,2 +1,2 @@ -/** Environnement cible (prod ou test). */ +/** Target game environment: production (`universe`) or testing (`universe-testing`). */ export type Env = 'universe' | 'universe-testing' diff --git a/src/shared/types/game.ts b/src/shared/types/game.ts index a787403..0fa6372 100644 --- a/src/shared/types/game.ts +++ b/src/shared/types/game.ts @@ -1,3 +1,4 @@ +/** Normalized server operational status from the status API. */ export type ServerStatusValue = | 'online' | 'degraded' @@ -6,6 +7,7 @@ export type ServerStatusValue = | 'unknown' | 'unavailable' +/** Server status and player count returned to the renderer. */ export interface GameStatusResult { status: ServerStatusValue statusLabel: string diff --git a/src/shared/types/install.ts b/src/shared/types/install.ts index 7ed14e2..ecca8e9 100644 --- a/src/shared/types/install.ts +++ b/src/shared/types/install.ts @@ -1,3 +1,4 @@ +/** Version metadata returned after a successful game install or update. */ export interface InstallResult { version: string releaseDate: string diff --git a/src/shared/types/installProgress.ts b/src/shared/types/installProgress.ts new file mode 100644 index 0000000..438f212 --- /dev/null +++ b/src/shared/types/installProgress.ts @@ -0,0 +1,11 @@ +/** Install/download progress label sent from main → renderer (translated in UI). */ +export type InstallProgressLabel = + | { key: 'connecting' } + | { key: 'checkingUpdates' } + | { key: 'downloading'; downloaded: number; total: number } + | { key: 'downloadingIndeterminate'; downloaded: number } + | { key: 'preparing' } + | { key: 'extracting'; current: number; total: number } + | { key: 'cleaning' } + | { key: 'completeInstall'; version: string } + | { key: 'completeUpdate'; version: string } diff --git a/src/shared/types/version.ts b/src/shared/types/version.ts index f9c923a..23d1a39 100644 --- a/src/shared/types/version.ts +++ b/src/shared/types/version.ts @@ -1,10 +1,12 @@ import type { Env } from './env' +/** Remote game build version and release date for one environment. */ export interface GameVersionInfo { version: string | null releaseDate: string | null } +/** Result of comparing local launcher and remote game/launcher versions. */ export interface VersionCheckResult { currentLauncherVersion: string latestLauncherVersion: string | null diff --git a/tailwind.config.ts b/tailwind.config.ts index 56d1a9f..8dca49c 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,21 +1,21 @@ export default { - content: ["./src/renderer/**/*.{ts,tsx}"], + content: ['./src/renderer/**/*.{ts,tsx}'], theme: { extend: { colors: { - "ds-bg": "#0d0d14", // fond principal - "ds-surface": "#16161f", // cards / panneaux - "ds-border": "#2a2a3a", // bordures - "ds-accent": "#6c63ff", // couleur principale (à ajuster) - "ds-text": "#e8e8f0", - "ds-muted": "#6b6b80", - "ds-success": "#22c55e", // serveurs OK - "ds-warning": "#f59e0b", // serveurs problèmes - "ds-danger": "#ef4444", // serveurs indispo + 'ds-bg': '#0d0d14', // fond principal + 'ds-surface': '#16161f', // cards / panneaux + 'ds-border': '#2a2a3a', // bordures + 'ds-accent': '#6c63ff', // couleur principale (à ajuster) + 'ds-text': '#e8e8f0', + 'ds-muted': '#6b6b80', + 'ds-success': '#22c55e', // serveurs OK + 'ds-warning': '#f59e0b', // serveurs problèmes + 'ds-danger': '#ef4444' // serveurs indispo }, fontFamily: { - sans: ["Poppins", "system-ui", "sans-serif"], - }, - }, - }, -} \ No newline at end of file + sans: ['Poppins', 'system-ui', 'sans-serif'] + } + } + } +} From 9f0935caedbea0cc8c64ad5291a580875c523912 Mon Sep 17 00:00:00 2001 From: maestrix Date: Fri, 15 May 2026 23:50:40 +0200 Subject: [PATCH 3/5] fix: game files not verified if files are deleted --- src/main/services/auth/tokenStorage.ts | 14 ++++++- src/main/services/download/install.ts | 3 ++ src/main/services/download/manifest.ts | 16 +++++--- src/main/services/game/handlers.ts | 6 ++- src/main/services/game/paths.ts | 10 +++++ src/renderer/components/panel/FilesPanel.tsx | 21 ++++++++-- src/renderer/components/panel/GamePanel.tsx | 6 ++- src/renderer/stores/files.ts | 40 ++++++++++++++++---- 8 files changed, 95 insertions(+), 21 deletions(-) diff --git a/src/main/services/auth/tokenStorage.ts b/src/main/services/auth/tokenStorage.ts index a19c68f..c75371c 100644 --- a/src/main/services/auth/tokenStorage.ts +++ b/src/main/services/auth/tokenStorage.ts @@ -1,22 +1,34 @@ import { app, safeStorage } from 'electron' import fs from 'fs' import path from 'path' +import log from 'electron-log' import type { Env } from '@shared/types/env' import type { TokenSet } from './types' +let loggedPlainFallback = false + /** Returns the encrypted token file path for the given environment. */ function getTokenPath(env: Env): string { const filename = env === 'universe' ? 'tokens.enc' : `tokens-${env}.enc` return path.join(app.getPath('userData'), filename) } +function logPlainFallbackOnce(): void { + if (loggedPlainFallback) return + loggedPlainFallback = true + log.info( + '[Auth] Token encryption unavailable on this system (common on Linux/WSL without a keyring). ' + + 'Tokens are stored as JSON in userData — use a supported OS keyring for encrypted storage.' + ) +} + /** Encrypts and writes the token set to disk. */ export function storeTokens(env: Env, tokens: TokenSet): void { const json = JSON.stringify(tokens) if (safeStorage.isEncryptionAvailable()) { fs.writeFileSync(getTokenPath(env), safeStorage.encryptString(json)) } else { - console.warn('[Auth] safeStorage unavailable, plain JSON fallback') + logPlainFallbackOnce() fs.writeFileSync(getTokenPath(env), json, 'utf-8') } } diff --git a/src/main/services/download/install.ts b/src/main/services/download/install.ts index 37f9f9b..b62b232 100644 --- a/src/main/services/download/install.ts +++ b/src/main/services/download/install.ts @@ -34,6 +34,9 @@ export async function downloadAndInstall( sendProgress(win, 99, { key: 'cleaning' }) fs.unlinkSync(zipFilePath) const manifest = await resolveInstalledVersion(env, gameRoot) + if (!manifest) { + throw new Error('Installation finished but version.json was not found in the game folder.') + } sendProgress(win, 100, { key: 'completeInstall', version: manifest.version }) return manifest } catch (err) { diff --git a/src/main/services/download/manifest.ts b/src/main/services/download/manifest.ts index 08eb4bc..65fd184 100644 --- a/src/main/services/download/manifest.ts +++ b/src/main/services/download/manifest.ts @@ -30,12 +30,11 @@ function findVersionManifestPath(gameRoot: string): string | null { return null } -/** Reads version metadata from the local `version.json` manifest. */ -function readVersionManifest(gameRoot: string): InstallResult { +/** Reads version metadata from the local `version.json` manifest, or null if missing. */ +function readVersionManifest(gameRoot: string): InstallResult | null { const manifestPath = findVersionManifestPath(gameRoot) if (!manifestPath) { - console.warn('[Download] version.json not found under', gameRoot) - return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } + return null } try { const json = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as { @@ -47,15 +46,20 @@ function readVersionManifest(gameRoot: string): InstallResult { releaseDate: json.releaseDate ?? new Date().toISOString().split('T')[0] } } catch { - return { version: 'unknown', releaseDate: new Date().toISOString().split('T')[0] } + return null } } /** * Returns installed version aligned with remote `/version` when available. + * Returns null when no local `version.json` is found. */ -export async function resolveInstalledVersion(env: Env, gameRoot: string): Promise { +export async function resolveInstalledVersion( + env: Env, + gameRoot: string +): Promise { const fromFile = readVersionManifest(gameRoot) + if (!fromFile) return null const fromApi = await fetchRemoteGameVersion(env) if (fromApi?.version) { return { diff --git a/src/main/services/game/handlers.ts b/src/main/services/game/handlers.ts index 5082e1b..cca01f5 100644 --- a/src/main/services/game/handlers.ts +++ b/src/main/services/game/handlers.ts @@ -5,7 +5,7 @@ import { downloadAndInstall } from '../download' import { resolveInstalledVersion } from '../download/manifest' import { clearDyingStarGodotCaches } from '../godotUserdataCache' import type { Env } from '@shared/types/env' -import { findChangelogPath, getGameRoot } from './paths' +import { findChangelogPath, getGameRoot, isGameInstalledAtPath } from './paths' import { launchGame } from './launch' import { isGameRunning, setLauncherWindow } from './processWatch' import { getInstallDialogStrings } from '../../l10n/dialogs' @@ -64,7 +64,9 @@ export function registerFilesHandlers(win: BrowserWindow): void { installPath: string ): Promise<{ version: string; releaseDate: string } | null> => { if (!installPath || typeof installPath !== 'string') return null - const gameRoot = getGameRoot(path.resolve(installPath)) + const resolvedPath = path.resolve(installPath) + if (!isGameInstalledAtPath(resolvedPath)) return null + const gameRoot = getGameRoot(resolvedPath) if (!fs.existsSync(gameRoot)) return null return resolveInstalledVersion(env, gameRoot) } diff --git a/src/main/services/game/paths.ts b/src/main/services/game/paths.ts index f00cc6d..20c71d8 100644 --- a/src/main/services/game/paths.ts +++ b/src/main/services/game/paths.ts @@ -9,6 +9,16 @@ export function getGameRoot(installPath: string): string { return path.join(installPath, GAME_INSTALL_SUBDIR) } +/** True when the platform executable (or legacy layout) is present on disk. */ +export function isGameInstalledAtPath(installPath: string): boolean { + if (!installPath?.trim()) return false + try { + return fs.existsSync(getExecutablePath(path.resolve(installPath))) + } catch { + return false + } +} + /** Resolves the platform game executable (payload dir first, then legacy install root). */ export function getExecutablePath(installPath: string): string { const exe = GAME_EXECUTABLES[process.platform] diff --git a/src/renderer/components/panel/FilesPanel.tsx b/src/renderer/components/panel/FilesPanel.tsx index a02fba6..c5b002d 100644 --- a/src/renderer/components/panel/FilesPanel.tsx +++ b/src/renderer/components/panel/FilesPanel.tsx @@ -17,7 +17,16 @@ import { toastDurationMs } from '@lib/env' /** Files panel: install path, install/update progress, changelog, and cache clear. */ export default function FilesPanel(): React.JSX.Element { const { activeEnv } = useEnvStore() - const { data, setInstallPath, selectDirectory, install, update, clearCache } = useFilesStore() + const { + data, + setInstallPath, + selectDirectory, + install, + update, + verify, + clearCache, + syncInstalledVersions + } = useFilesStore() const { available } = useAvailabilityStore() const { installed, version, releaseDate, installing, progress, progressEvent, installPath } = @@ -41,6 +50,10 @@ export default function FilesPanel(): React.JSX.Element { const [changelogMd, setChangelogMd] = useState(null) const [cacheToast, setCacheToast] = useState<'success' | 'partial' | 'error' | null>(null) + useEffect(() => { + void syncInstalledVersions() + }, [activeEnv, syncInstalledVersions]) + useEffect(() => { if (!cacheToast) return const id = window.setTimeout(() => setCacheToast(null), toastDurationMs()) @@ -155,11 +168,11 @@ export default function FilesPanel(): React.JSX.Element { )} - {/* {isAvailable && installed && !gameUpdateAvailable && ( - - )} */} + )} {isAvailable && installed && (