commit 1db5a9c295dde3a4a18134225dda39e0cd3ece11 Author: Hydra Date: Sun Apr 21 06:26:29 2024 +0100 feat: migrating to electron-vite diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..cf640d53 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..7946dbc1 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +MAIN_VITE_STEAMGRIDDB_API_KEY=YOUR_API_KEY +MAIN_VITE_ONLINEFIX_USERNAME=YOUR_USERNAME +MAIN_VITE_ONLINEFIX_PASSWORD=YOUR_PASSWORD +MAIN_VITE_SENTRY_DSN=YOUR_SENTRY_DSN +RENDERER_VITE_SENTRY_DSN=YOUR_SENTRY_DSN diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..a6f34fea --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +node_modules +dist +out +.gitignore diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..ed84d7c7 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "@electron-toolkit/eslint-config-ts/recommended", + "@electron-toolkit/eslint-config-prettier", + ], +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8a00eaf7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +out +.DS_Store +*.log* +.env diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..2ced9527 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ +shamefully-hoist=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..9c6b791d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +out +dist +pnpm-lock.yaml +LICENSE.md +tsconfig.json +tsconfig.*.json diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 00000000..f9711fb4 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,4 @@ +singleQuote: false +semi: true +tabWidth: 2 +trailingComma: es5 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..940260d8 --- /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 00000000..0b6b9a64 --- /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 00000000..0ff36a58 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "i18n-ally.localesPaths": ["src/locales"], + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..cdde5460 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# Hydra + +![Discord](https://img.shields.io/discord/1220692017311645737?style=flat&logo=discord&label=Hydra&labelColor=%231c1c1c) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/hydralauncher/hydra/build.yml) +![GitHub package.json version](https://img.shields.io/github/package-json/v/hydralauncher/hydra) + +Hydra is a game launcher with its own embedded bittorrent client and a self-managed repack scraper. +The launcher is written in TypeScript (Electron) and Python, which handles the torrenting system by using [libtorrent](https://www.libtorrent.org/). + +![Hydra Catalogue](./docs/screenshot.png) + +## Installation + +### Install Node.js + +Ensure you have Node.js installed on your machine. If not, download and install it from [nodejs.org](https://nodejs.org/). + +### Install Yarn + +Yarn is a package manager for Node.js. If you haven't installed Yarn yet, you can do so by following the instructions on [yarnpkg.com](https://classic.yarnpkg.com/lang/en/docs/install/). + +### Clone the Repository + +```bash +git clone https://github.com/hydralauncher/hydra.git +``` + +### Install Node Dependencies + +Navigate to the project directory and install the Node dependencies using Yarn: + +```bash +cd hydra +yarn +``` + +### Install Python 3.9 + +Ensure you have Python installed on your machine. You can download and install it from [python.org](https://www.python.org/downloads/release/python-3919/). + +### Install Python Dependencies + +Install the required Python dependencies using pip: + +```bash +pip install -r requirements.txt +``` + +## Environment variables + +You'll need an SteamGridDB API Key in order to fetch the game icons on installation. +If you want to have onlinefix as a repacker you'll need to add your credentials to the .env + +Once you have it, you can paste the `.env.example` file and put it on `STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`. + +## Running + +Once you've got all things set up, you can run the following command to start both the Electron process and the bittorrent client: + +```bash +yarn start +``` + +## Build + +### Build the bittorrent client + +Build the bittorrent client by using this command: + +```bash +python torrent-client/setup.py build +``` + +### Build the Electron application + +Build the Electron application by using this command: + +```bash +yarn make +``` + +## Contributors + + + + + +Made with [contrib.rocks](https://contrib.rocks). + +## License + +Hydra is licensed under the [MIT License](LICENSE). diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 00000000..38c887b2 --- /dev/null +++ b/build/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/build/icon.icns b/build/icon.icns new file mode 100644 index 00000000..28644aa9 Binary files /dev/null and b/build/icon.icns differ diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 00000000..72c391eb Binary files /dev/null and b/build/icon.ico differ diff --git a/build/icon.png b/build/icon.png new file mode 100644 index 00000000..cf9e8b2c Binary files /dev/null and b/build/icon.png differ diff --git a/docs/screenshot.png b/docs/screenshot.png new file mode 100644 index 00000000..b98fab75 Binary files /dev/null and b/docs/screenshot.png differ diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 00000000..17439d70 --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,45 @@ +appId: site.hydralauncher.hydra +productName: Hydra +directories: + buildResources: build +files: + - "!**/.vscode/*" + - "!src/*" + - "!electron.vite.config.{js,ts,mjs,cjs}" + - "!{.eslintignore,.eslintrc.cjs,.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: Hydra +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 + maintainer: electronjs.org + category: Utility +appImage: + artifactName: ${name}-${version}.${ext} +npmRebuild: false +publish: + provider: generic + url: https://example.com/auto-updates +electronDownload: + mirror: https://npmmirror.com/mirrors/electron/ diff --git a/electron.vite.config.ts b/electron.vite.config.ts new file mode 100644 index 00000000..516d40a7 --- /dev/null +++ b/electron.vite.config.ts @@ -0,0 +1,43 @@ +import { resolve } from "path"; +import { + defineConfig, + loadEnv, + swcPlugin, + externalizeDepsPlugin, +} from "electron-vite"; +import react from "@vitejs/plugin-react"; +import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin"; +import svgr from "vite-plugin-svgr"; + +export default defineConfig(({ command, mode }) => { + loadEnv(mode); + + return { + main: { + build: { + rollupOptions: { + external: ["better-sqlite3"], + }, + }, + resolve: { + alias: { + "@main": resolve("src/main"), + "@locales": resolve("src/locales"), + }, + }, + plugins: [externalizeDepsPlugin(), swcPlugin()], + }, + preload: { + plugins: [externalizeDepsPlugin()], + }, + renderer: { + resolve: { + alias: { + "@renderer": resolve("src/renderer/src"), + "@locales": resolve("src/locales"), + }, + }, + plugins: [svgr(), react(), vanillaExtractPlugin()], + }, + }; +}); diff --git a/package.json b/package.json new file mode 100644 index 00000000..65c73c10 --- /dev/null +++ b/package.json @@ -0,0 +1,81 @@ +{ + "name": "hydra", + "version": "1.0.0", + "description": "An Electron application with React and TypeScript", + "main": "./out/main/index.js", + "author": "example.com", + "homepage": "https://electron-vite.org", + "type": "module", + "scripts": { + "format": "prettier --write .", + "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --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", + "start": "electron-vite preview", + "dev": "electron-vite dev", + "build": "npm run typecheck && electron-vite build", + "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" + }, + "dependencies": { + "@electron-toolkit/preload": "^3.0.0", + "@electron-toolkit/utils": "^3.0.0", + "@fontsource/fira-mono": "^5.0.13", + "@fontsource/fira-sans": "^5.0.20", + "@primer/octicons-react": "^19.9.0", + "@reduxjs/toolkit": "^2.2.3", + "@sentry/electron": "^4.23.0", + "@sentry/react": "^7.111.0", + "@vanilla-extract/css": "^1.14.2", + "@vanilla-extract/recipes": "^0.5.2", + "axios": "^1.6.8", + "better-sqlite3": "^9.5.0", + "check-disk-space": "^3.4.0", + "classnames": "^2.5.1", + "color.js": "^1.2.0", + "date-fns": "^3.6.0", + "flexsearch": "^0.7.43", + "i18next": "^23.11.2", + "i18next-browser-languagedetector": "^7.2.1", + "jsdom": "^24.0.0", + "lodash-es": "^4.17.21", + "lottie-react": "^2.4.0", + "parse-torrent": "^11.0.16", + "ps-list": "^8.1.1", + "react-i18next": "^14.1.0", + "react-loading-skeleton": "^3.4.0", + "react-redux": "^9.1.1", + "react-router-dom": "^6.22.3", + "tasklist": "^5.0.0", + "typeorm": "^0.3.20", + "winston": "^3.13.0", + "yaml": "^2.4.1" + }, + "devDependencies": { + "@electron-toolkit/eslint-config-prettier": "^2.0.0", + "@electron-toolkit/eslint-config-ts": "^1.0.1", + "@electron-toolkit/tsconfig": "^1.0.1", + "@swc/core": "^1.4.16", + "@types/lodash-es": "^4.17.12", + "@types/node": "^18.19.9", + "@types/react": "^18.2.48", + "@types/react-dom": "^18.2.18", + "@vanilla-extract/vite-plugin": "^4.0.7", + "@vitejs/plugin-react": "^4.2.1", + "electron": "^28.2.0", + "electron-builder": "^24.9.1", + "electron-vite": "^2.0.0", + "eslint": "^8.56.0", + "eslint-plugin-react": "^7.33.2", + "prettier": "^3.2.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "typescript": "^5.3.3", + "vite": "^5.0.12", + "vite-plugin-svgr": "^4.2.0" + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..6cee730a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +libtorrent +cx_Freeze +cx_Logging; sys_platform == 'win32' +lief; sys_platform == 'win32' +pywin32; sys_platform == 'win32' diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 00000000..cf9e8b2c Binary files /dev/null and b/resources/icon.png differ diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json new file mode 100644 index 00000000..2f967b76 --- /dev/null +++ b/src/locales/en/translation.json @@ -0,0 +1,141 @@ +{ + "home": { + "featured": "Featured", + "recently_added": "Recently added", + "trending": "Trending", + "surprise_me": "Surprise me", + "no_results": "No results found" + }, + "sidebar": { + "catalogue": "Catalogue", + "downloads": "Downloads", + "settings": "Settings", + "my_library": "My library", + "downloading_metadata": "{{title}} (Downloading metadata…)", + "checking_files": "{{title}} ({{percentage}} - Checking files…)", + "paused": "{{title}} (Paused)", + "downloading": "{{title}} ({{percentage}} - Downloading…)", + "filter": "Filter library", + "follow_us": "Follow us", + "home": "Home" + }, + "header": { + "search": "Search", + "home": "Home", + "catalogue": "Catalogue", + "downloads": "Downloads", + "search_results": "Search results", + "settings": "Settings" + }, + "bottom_panel": { + "no_downloads_in_progress": "No downloads in progress", + "downloading_metadata": "Downloading {{title}} metadata…", + "checking_files": "Checking {{title}} files… ({{percentage}} complete)", + "downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}" + }, + "catalogue": { + "next_page": "Next page", + "previous_page": "Previous page" + }, + "game_details": { + "open_download_options": "Open download options", + "download_options_zero": "No download option", + "download_options_one": "{{count}} download option", + "download_options_other": "{{count}} download options", + "updated_at": "Updated {{updated_at}}", + "install": "Install", + "resume": "Resume", + "pause": "Pause", + "cancel": "Cancel", + "remove": "Remove", + "remove_from_list": "Remove", + "space_left_on_disk": "{{space}} left on disk", + "eta": "Conclusion {{eta}}", + "downloading_metadata": "Downloading metadata…", + "checking_files": "Checking files…", + "filter": "Filter repacks", + "requirements": "System requirements", + "minimum": "Minimum", + "recommended": "Recommended", + "no_minimum_requirements": "{{title}} doesn't provide minimum requirements information", + "no_recommended_requirements": "{{title}} doesn't provide recommended requirements information", + "paused_progress": "{{progress}} (Paused)", + "release_date": "Released in {{date}}", + "publisher": "Published by {{publisher}}", + "copy_link_to_clipboard": "Copy link", + "copied_link_to_clipboard": "Link copied", + "hours": "hours", + "minutes": "minutes", + "accuracy": "{{accuracy}}% accuracy", + "add_to_library": "Add to library", + "remove_from_library": "Remove from library", + "no_downloads": "No downloads available", + "play_time": "Played for {{amount}}", + "last_time_played": "Last played {{period}}", + "not_played_yet": "You haven't played {{title}} yet", + "next_suggestion": "Next suggestion", + "play": "Play", + "deleting": "Deleting installer…", + "close": "Close", + "playing_now": "Playing now" + }, + "activation": { + "title": "Activate Hydra", + "installation_id": "Installation ID:", + "enter_activation_code": "Enter your activation code", + "message": "If you don't know where to ask for this, then you shouldn't have this.", + "activate": "Activate", + "loading": "Loading…" + }, + "downloads": { + "resume": "Resume", + "pause": "Pause", + "eta": "Conclusion {{eta}}", + "paused": "Paused", + "verifying": "Verifying…", + "completed_at": "Completed in {{date}}", + "completed": "Completed", + "cancelled": "Cancelled", + "download_again": "Download again", + "cancel": "Cancel", + "filter": "Filter downloaded games", + "remove": "Remove", + "downloading_metadata": "Downloading metadata…", + "checking_files": "Checking files…", + "starting_download": "Starting download…", + "deleting": "Deleting installer…", + "delete": "Remove installer", + "remove_from_list": "Remove", + "delete_modal_title": "Are you sure?", + "delete_modal_description": "This will remove all the installation files from your computer", + "install": "Install" + }, + "settings": { + "downloads_path": "Downloads path", + "change": "Update", + "notifications": "Notifications", + "enable_download_notifications": "When a download is complete", + "enable_repack_list_notifications": "When a new repack is added", + "telemetry": "Telemetry", + "telemetry_description": "Enable anonymous usage statistics" + }, + "notifications": { + "download_complete": "Download complete", + "game_ready_to_install": "{{title}} is ready to install", + "repack_list_updated": "Repack list updated", + "repack_count_one": "{{count}} repack added", + "repack_count_other": "{{count}} repacks added" + }, + "system_tray": { + "open": "Open Hydra", + "quit": "Quit" + }, + "game_card": { + "no_downloads": "No downloads available" + }, + "binary_not_found_modal": { + "title": "Programs not installed", + "description": "Wine or Lutris executables were not found on your system", + "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" + } +} diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json new file mode 100644 index 00000000..d28fccc4 --- /dev/null +++ b/src/locales/es/translation.json @@ -0,0 +1,141 @@ +{ + "home": { + "featured": "Destacado", + "recently_added": "Recién Añadidos", + "trending": "Tendencias", + "surprise_me": "¡Sorpréndeme!", + "no_results": "No se encontraron resultados" + }, + "sidebar": { + "catalogue": "Catálogo", + "downloads": "Descargas", + "settings": "Ajustes", + "my_library": "Mi biblioteca", + "downloading_metadata": "{{title}} (Descargando metadatos…)", + "checking_files": "{{title}} ({{percentage}} - Analizando archivos…)", + "paused": "{{title}} (Pausado)", + "downloading": "{{title}} ({{percentage}} - Descargando…)", + "filter": "Filtrar biblioteca", + "home": "Hogar", + "follow_us": "Síganos" + }, + "header": { + "search": "Buscar", + "catalogue": "Catálogo", + "downloads": "Descargas", + "search_results": "Resultados de búsqueda", + "settings": "Ajustes", + "home": "Início" + }, + "bottom_panel": { + "no_downloads_in_progress": "Sin descargas en progreso", + "downloading_metadata": "Descargando metadatos de {{title}}…", + "checking_files": "Analizando archivos de {{title}} - ({{percentage}} completado)", + "downloading": "Descargando {{title}}… ({{percentage}} completado) - Finalizando {{eta}} - {{speed}}" + }, + "game_details": { + "open_download_options": "Ver opciones de descargas", + "download_options_zero": "No hay opciones de descargas disponibles", + "download_options_one": "{{count}} opción de descarga", + "download_options_other": "{{count}} opciones de descargas", + "updated_at": "Actualizado el {{updated_at}}", + "resume": "Continuar", + "pause": "Pausa", + "cancel": "Cancelar", + "remove": "Eliminar", + "remove_from_list": "Quitar", + "space_left_on_disk": "{{space}} restantes en el disco", + "eta": "Finalizando {{eta}}", + "downloading_metadata": "Descargando metadatos…", + "checking_files": "Analizando archivos…", + "filter": "Filtrar repacks", + "requirements": "Requisitos del Sistema", + "minimum": "Mínimos", + "recommended": "Recomendados", + "no_minimum_requirements": "Sin requisitos mínimos para {{title}}", + "no_recommended_requirements": "{{title}} no tiene requisitos recomendados", + "paused_progress": "{{progress}} (Pausado)", + "release_date": "Fecha de lanzamiento {{date}}", + "publisher": "Publicado por {{publisher}}", + "copy_link_to_clipboard": "Copiar enlace", + "copied_link_to_clipboard": "Enlace copiado", + "hours": "horas", + "minutes": "minutos", + "accuracy": "{{accuracy}}% precisión", + "add_to_library": "Agregar a la biblioteca", + "remove_from_library": "Eliminar de la biblioteca", + "no_downloads": "No hay descargas disponibles", + "next_suggestion": "Siguiente sugerencia", + "play_time": "Jugado por {{cantidad}}", + "install": "Instalar", + "play": "Jugar", + "not_played_yet": "Aún no has jugado a {{title}}", + "close": "Cerca", + "deleting": "Eliminando instalador…", + "playing_now": "Jugando ahora", + "last_time_played": "Jugado por última vez {{period}}" + }, + "activation": { + "title": "Activar Hydra", + "installation_id": "ID de la Instalación:", + "enter_activation_code": "Introduce tu código de activación", + "message": "Si no sabes donde obtener el código, no deberías de tener esto.", + "activate": "Activar", + "loading": "Cargando…" + }, + "downloads": { + "resume": "Resumir", + "pause": "Pausa", + "eta": "Finalizando {{eta}}", + "paused": "En Pausa", + "verifying": "Verificando…", + "completed_at": "Completado el {{date}}", + "completed": "Completado", + "cancelled": "Cancelado", + "download_again": "Descargar de nuevo", + "cancel": "Cancelar", + "filter": "Buscar juegos descargados", + "remove": "Eliminar", + "downloading_metadata": "Descargando metadatos…", + "checking_files": "Verificando archivos…", + "starting_download": "Iniciando descarga…", + "remove_from_list": "Eliminar", + "delete": "Quitar instalador", + "delete_modal_description": "Esto eliminará todos los archivos de instalación de su computadora.", + "delete_modal_title": "¿Está seguro?", + "deleting": "Eliminando instalador…", + "install": "Instalar" + }, + "settings": { + "downloads_path": "Ruta de descarga", + "change": "Cambiar", + "notifications": "Notificaciones", + "enable_download_notifications": "Cuando se completa una descarga", + "enable_repack_list_notifications": "Cuando se añade un repack nuevo", + "telemetry": "Telemetria", + "telemetry_description": "Habilitar estadísticas de uso anónimas" + }, + "notifications": { + "download_complete": "Descarga completada", + "game_ready_to_install": "{{title}} está listo para instalarse", + "repack_list_updated": "Lista de repacks actualizadas", + "repack_count_one": "{{count}} repack ha sido añadido", + "repack_count_other": "{{count}} repacks añadidos" + }, + "system_tray": { + "open": "Abrir Hydra", + "quit": "Salir" + }, + "game_card": { + "no_downloads": "No hay descargas disponibles" + }, + "binary_not_found_modal": { + "title": "Programas no instalados", + "description": "Los ejecutables de Wine o Lutris no se encontraron en su sistema", + "instructions": "Comprueba la forma correcta de instalar cualquiera de ellos en tu distro Linux para que el juego pueda ejecutarse con normalidad" + }, + "catalogue": { + "next_page": "Siguiente página", + "previous_page": "Pagina anterior" + } +} diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json new file mode 100644 index 00000000..e3d2bd3d --- /dev/null +++ b/src/locales/fr/translation.json @@ -0,0 +1,141 @@ +{ + "home": { + "featured": "En vedette", + "recently_added": "Récemment ajouté", + "trending": "Tendance", + "surprise_me": "Surprenez-moi", + "no_results": "Aucun résultat trouvé" + }, + "sidebar": { + "catalogue": "Catalogue", + "downloads": "Téléchargements", + "settings": "Paramètres", + "my_library": "Ma bibliothèque", + "downloading_metadata": "{{title}} (Téléchargement des métadonnées…)", + "checking_files": "{{title}} ({{percentage}} - Vérification des fichiers…)", + "paused": "{{title}} (En pause)", + "downloading": "{{title}} ({{percentage}} - Téléchargement en cours…)", + "filter": "Filtrer la bibliothèque", + "home": "Page d’accueil", + "follow_us": "Suivez-nous" + }, + "header": { + "search": "Recherche", + "catalogue": "Catalogue", + "downloads": "Téléchargements", + "search_results": "Résultats de la recherche", + "settings": "Paramètres", + "home": "Maison" + }, + "bottom_panel": { + "no_downloads_in_progress": "Aucun téléchargement en cours", + "downloading_metadata": "Téléchargement des métadonnées de {{title}}…", + "checking_files": "Vérification des fichiers de {{title}}… ({{percentage}} complet)", + "downloading": "Téléchargement de {{title}}… ({{percentage}} complet) - Conclusion dans {{eta}} - {{speed}}" + }, + "game_details": { + "open_download_options": "Ouvrir les options de téléchargement", + "download_options_zero": "Aucune option de téléchargement", + "download_options_one": "{{count}} option de téléchargement", + "download_options_other": "{{count}} options de téléchargement", + "updated_at": "Mis à jour le {{updated_at}}", + "resume": "Reprendre", + "pause": "Pause", + "cancel": "Annuler", + "remove": "Supprimer", + "remove_from_list": "Retirer", + "space_left_on_disk": "{{space}} restant sur le disque", + "eta": "Conclusion dans {{eta}}", + "downloading_metadata": "Téléchargement des métadonnées en cours…", + "checking_files": "Vérification des fichiers…", + "filter": "Filtrer les réductions", + "requirements": "Configuration requise", + "minimum": "Minimum", + "recommended": "Recommandée", + "no_minimum_requirements": "{{title}} ne fournit pas d'informations sur les exigences minimales", + "no_recommended_requirements": "{{title}} ne fournit pas d'informations sur les exigences recommandées", + "paused_progress": "{{progress}} (En pause)", + "release_date": "Sorti le {{date}}", + "publisher": "Édité par {{publisher}}", + "copy_link_to_clipboard": "Copier le lien", + "copied_link_to_clipboard": "Lien copié", + "hours": "heures", + "minutes": "minutes", + "accuracy": "{{accuracy}}% précision", + "add_to_library": "Ajouter à la bibliothèque", + "remove_from_library": "Supprimer de la bibliothèque", + "no_downloads": "Aucun téléchargement disponible", + "next_suggestion": "Suggestion suivante", + "play_time": "Joué pour {{montant}}", + "install": "Installer", + "play": "Jouer", + "not_played_yet": "Vous n'avez pas encore joué à {{title}}", + "close": "Fermer", + "deleting": "Suppression du programme d'installation…", + "playing_now": "Je joue maintenant", + "last_time_played": "Dernière lecture {{période}}" + }, + "activation": { + "title": "Activer Hydra", + "installation_id": "ID d'installation :", + "enter_activation_code": "Entrez votre code d'activation", + "message": "Si vous ne savez pas où demander cela, vous ne devriez pas l'avoir.", + "activate": "Activer", + "loading": "Chargement en cours…" + }, + "downloads": { + "resume": "Reprendre", + "pause": "Pause", + "eta": "Conclusion dans {{eta}}", + "paused": "En pause", + "verifying": "Vérification en cours…", + "completed_at": "Terminé en {{date}}", + "completed": "Terminé", + "cancelled": "Annulé", + "download_again": "Télécharger à nouveau", + "cancel": "Annuler", + "filter": "Filtrer les jeux téléchargés", + "remove": "Supprimer", + "downloading_metadata": "Téléchargement des métadonnées en cours…", + "checking_files": "Vérification des fichiers…", + "starting_download": "Démarrage du téléchargement…", + "remove_from_list": "Retirer", + "delete": "Supprimer le programme d'installation", + "delete_modal_description": "Cela supprimera tous les fichiers d'installation de votre ordinateur", + "delete_modal_title": "Es-tu sûr?", + "deleting": "Suppression du programme d'installation…", + "install": "Installer" + }, + "settings": { + "downloads_path": "Chemin des téléchargements", + "change": "Mettre à jour", + "notifications": "Notifications", + "enable_download_notifications": "Quand un téléchargement est terminé", + "enable_repack_list_notifications": "Quand une nouvelle réduction est ajoutée", + "telemetry": "Télémétrie", + "telemetry_description": "Activer les statistiques d'utilisation anonymes" + }, + "notifications": { + "download_complete": "Téléchargement terminé", + "game_ready_to_install": "{{title}} est prêt à être installé", + "repack_list_updated": "Liste de réductions mise à jour", + "repack_count_one": "{{count}} réduction ajoutée", + "repack_count_other": "{{count}} réductions ajoutées" + }, + "system_tray": { + "open": "Ouvrir Hydra", + "quit": "Quitter" + }, + "game_card": { + "no_downloads": "Aucun téléchargement disponible" + }, + "binary_not_found_modal": { + "description": "Les exécutables Wine ou Lutris sont introuvables sur votre système", + "instructions": "Vérifiez la bonne façon d'installer l'un d'entre eux sur votre distribution Linux afin que le jeu puisse fonctionner normalement", + "title": "Programmes non installés" + }, + "catalogue": { + "next_page": "Page suivante", + "previous_page": "Page précédente" + } +} diff --git a/src/locales/index.ts b/src/locales/index.ts new file mode 100644 index 00000000..98a93c78 --- /dev/null +++ b/src/locales/index.ts @@ -0,0 +1,4 @@ +export { default as en } from "./en/translation.json"; +export { default as pt } from "./pt/translation.json"; +export { default as es } from "./es/translation.json"; +export { default as fr } from "./fr/translation.json"; diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json new file mode 100644 index 00000000..3498ad83 --- /dev/null +++ b/src/locales/pt/translation.json @@ -0,0 +1,141 @@ +{ + "home": { + "featured": "Destaque", + "recently_added": "Novidades", + "trending": "Populares", + "surprise_me": "Me surpreenda", + "no_results": "Nenhum resultado encontrado" + }, + "sidebar": { + "catalogue": "Catálogo", + "downloads": "Downloads", + "settings": "Configurações", + "my_library": "Minha biblioteca", + "downloading_metadata": "{{title}} (Baixando metadados…)", + "checking_files": "{{title}} ({{percentage}} - Verificando arquivos…)", + "paused": "{{title}} (Pausado)", + "downloading": "{{title}} ({{percentage}} - Baixando…)", + "filter": "Filtrar biblioteca", + "home": "Início", + "follow_us": "Acompanhe-nos" + }, + "header": { + "search": "Buscar", + "catalogue": "Catálogo", + "downloads": "Downloads", + "search_results": "Resultados da busca", + "settings": "Configurações", + "home": "Início" + }, + "bottom_panel": { + "no_downloads_in_progress": "Sem downloads em andamento", + "downloading_metadata": "Baixando metadados de {{title}}…", + "checking_files": "Verificando arquivos de {{title}}… ({{percentage}} completo)", + "downloading": "Baixando {{title}}… ({{percentage}} completo) - Conclusão {{eta}} - {{speed}}" + }, + "game_details": { + "open_download_options": "Ver opções de download", + "download_options_zero": "Sem opções de download", + "download_options_one": "{{count}} opção de download", + "download_options_other": "{{count}} opções de download", + "updated_at": "Atualizado {{updated_at}}", + "resume": "Resumir", + "pause": "Pausar", + "cancel": "Cancelar", + "remove": "Remover", + "remove_from_list": "Remover", + "space_left_on_disk": "{{space}} livres em disco", + "eta": "Conclusão {{eta}}", + "downloading_metadata": "Baixando metadados…", + "checking_files": "Verificando arquivos…", + "filter": "Filtrar repacks", + "requirements": "Requisitos do sistema", + "minimum": "Mínimos", + "recommended": "Recomendados", + "no_minimum_requirements": "{{title}} não possui informações de requisitos mínimos", + "no_recommended_requirements": "{{title}} não possui informações de requisitos recomendados", + "paused_progress": "{{progress}} (Pausado)", + "release_date": "Lançado em {{date}}", + "publisher": "Publicado por {{publisher}}", + "copy_link_to_clipboard": "Copiar link", + "copied_link_to_clipboard": "Link copiado", + "hours": "horas", + "minutes": "minutos", + "accuracy": "{{accuracy}}% de precisão", + "add_to_library": "Adicionar à biblioteca", + "remove_from_library": "Remover da biblioteca", + "no_downloads": "Nenhum download disponível", + "play_time": "Jogado por {{amount}}", + "next_suggestion": "Próxima sugestão", + "install": "Instalar", + "last_time_played": "Jogou por último {{period}}", + "play": "Jogar", + "not_played_yet": "Você ainda não jogou {{title}}", + "close": "Fechar", + "deleting": "Excluindo instalador…", + "playing_now": "Jogando agora" + }, + "activation": { + "title": "Ativação", + "installation_id": "ID da instalação:", + "enter_activation_code": "Insira seu código de ativação", + "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.", + "activate": "Ativar", + "loading": "Carregando…" + }, + "downloads": { + "resume": "Resumir", + "pause": "Pausar", + "eta": "Conclusão {{eta}}", + "paused": "Pausado", + "verifying": "Verificando…", + "completed_at": "Concluído em {{date}}", + "completed": "Concluído", + "cancelled": "Cancelado", + "download_again": "Baixar novamente", + "cancel": "Cancelar", + "filter": "Filtrar jogos baixados", + "remove": "Remover", + "downloading_metadata": "Baixando metadados…", + "checking_files": "Verificando arquivos…", + "starting_download": "Iniciando download…", + "remove_from_list": "Remover", + "delete": "Remover instalador", + "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador", + "delete_modal_title": "Tem certeza?", + "deleting": "Excluindo instalador…", + "install": "Instalar" + }, + "settings": { + "downloads_path": "Diretório dos downloads", + "change": "Mudar", + "notifications": "Notificações", + "enable_download_notifications": "Quando um download for concluído", + "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", + "telemetry": "Telemetria", + "telemetry_description": "Habilitar estatísticas de uso anônimas" + }, + "notifications": { + "download_complete": "Download concluído", + "game_ready_to_install": "{{title}} está pronto para ser instalado", + "repack_list_updated": "Lista de repacks atualizada", + "repack_count_one": "{{count}} novo repack", + "repack_count_other": "{{count}} novos repacks" + }, + "system_tray": { + "open": "Abrir Hydra", + "quit": "Fechar" + }, + "game_card": { + "no_downloads": "Sem downloads disponíveis" + }, + "binary_not_found_modal": { + "title": "Programas não instalados", + "description": "Não foram encontrados no seu sistema os executáveis do Wine ou Lutris", + "instructions": "Verifique a forma correta de instalar algum deles na sua distro Linux para que o jogo possa ser executado normalmente" + }, + "catalogue": { + "next_page": "Próxima página", + "previous_page": "Página anterior" + } +} diff --git a/src/main/constants.ts b/src/main/constants.ts new file mode 100644 index 00000000..43d9d44e --- /dev/null +++ b/src/main/constants.ts @@ -0,0 +1,55 @@ +import { app } from "electron"; +import os from "node:os"; +import path from "node:path"; + +export const repackersOn1337x = [ + "DODI", + "FitGirl", + "0xEMPRESS", + "KaOsKrew", + "TinyRepacks", +] as const; + +export const repackers = [ + ...repackersOn1337x, + "Xatab", + "CPG", + "TinyRepacks", + "GOG", + "onlinefix", +] as const; + +export const months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +]; + +export enum GameStatus { + Seeding = "seeding", + Downloading = "downloading", + Paused = "paused", + CheckingFiles = "checking_files", + DownloadingMetadata = "downloading_metadata", + Cancelled = "cancelled", +} + +export const defaultDownloadsPath = path.join(os.homedir(), "downloads"); + +export const databasePath = path.join( + app.getPath("appData"), + app.getName(), + "hydra.db" +); + +export const INSTALLATION_ID_LENGTH = 6; +export const ACTIVATION_KEY_MULTIPLIER = 7; diff --git a/src/main/data-source.ts b/src/main/data-source.ts new file mode 100644 index 00000000..a2ca976c --- /dev/null +++ b/src/main/data-source.ts @@ -0,0 +1,35 @@ +import { DataSource } from "typeorm"; +import { + Game, + GameShopCache, + ImageCache, + Repack, + RepackerFriendlyName, + UserPreferences, + MigrationScript, + SteamGame, +} from "@main/entity"; +import type { SqliteConnectionOptions } from "typeorm/driver/sqlite/SqliteConnectionOptions"; + +import { databasePath } from "./constants"; + +export const createDataSource = (options: Partial) => + new DataSource({ + type: "better-sqlite3", + database: databasePath, + entities: [ + Game, + ImageCache, + Repack, + RepackerFriendlyName, + UserPreferences, + GameShopCache, + MigrationScript, + SteamGame, + ], + ...options, + }); + +export const dataSource = createDataSource({ + synchronize: true, +}); diff --git a/src/main/entity/game-shop-cache.entity.ts b/src/main/entity/game-shop-cache.entity.ts new file mode 100644 index 00000000..f83e1b0c --- /dev/null +++ b/src/main/entity/game-shop-cache.entity.ts @@ -0,0 +1,32 @@ +import { + Entity, + PrimaryColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; +import type { GameShop } from "@types"; + +@Entity("game_shop_cache") +export class GameShopCache { + @PrimaryColumn("text", { unique: true }) + objectID: string; + + @Column("text") + shop: GameShop; + + @Column("text", { nullable: true }) + serializedData: string; + + @Column("text", { nullable: true }) + howLongToBeatSerializedData: string; + + @Column("text", { nullable: true }) + language: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/game.entity.ts b/src/main/entity/game.entity.ts new file mode 100644 index 00000000..b1598b8b --- /dev/null +++ b/src/main/entity/game.entity.ts @@ -0,0 +1,69 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + OneToOne, + JoinColumn, +} from "typeorm"; +import type { GameShop } from "@types"; +import { Repack } from "./repack.entity"; + +@Entity("game") +export class Game { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { unique: true }) + objectID: string; + + @Column("text") + title: string; + + @Column("text") + iconUrl: string; + + @Column("text", { nullable: true }) + folderName: string | null; + + @Column("text", { nullable: true }) + downloadPath: string | null; + + @Column("text", { nullable: true }) + executablePath: string | null; + + @Column("int", { default: 0 }) + playTimeInMilliseconds: number; + + @Column("text") + shop: GameShop; + + @Column("text", { nullable: true }) + status: string; + + @Column("float", { default: 0 }) + progress: number; + + @Column("float", { default: 0 }) + fileVerificationProgress: number; + + @Column("int", { default: 0 }) + bytesDownloaded: number; + + @Column("text", { nullable: true }) + lastTimePlayed: Date | null; + + @Column("float", { default: 0 }) + fileSize: number; + + @OneToOne(() => Repack, { nullable: true }) + @JoinColumn() + repack: Repack; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/image-cache.entity.ts b/src/main/entity/image-cache.entity.ts new file mode 100644 index 00000000..a795c0d8 --- /dev/null +++ b/src/main/entity/image-cache.entity.ts @@ -0,0 +1,25 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity("image_cache") +export class ImageCache { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { unique: true }) + url: string; + + @Column("text") + data: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/index.ts b/src/main/entity/index.ts new file mode 100644 index 00000000..e93ba92e --- /dev/null +++ b/src/main/entity/index.ts @@ -0,0 +1,8 @@ +export * from "./game.entity"; +export * from "./image-cache.entity"; +export * from "./repack.entity"; +export * from "./repacker-friendly-name.entity"; +export * from "./user-preferences.entity"; +export * from "./game-shop-cache.entity"; +export * from "./migration-script.entity"; +export * from "./steam-game.entity"; diff --git a/src/main/entity/migration-script.entity.ts b/src/main/entity/migration-script.entity.ts new file mode 100644 index 00000000..d4166b71 --- /dev/null +++ b/src/main/entity/migration-script.entity.ts @@ -0,0 +1,22 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity("migration_script") +export class MigrationScript { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { unique: true }) + version: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts new file mode 100644 index 00000000..b87925a7 --- /dev/null +++ b/src/main/entity/repack.entity.ts @@ -0,0 +1,37 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity("repack") +export class Repack { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { unique: true }) + title: string; + + @Column("text", { unique: true }) + magnet: string; + + @Column("int") + page: number; + + @Column("text") + repacker: string; + + @Column("text") + fileSize: string; + + @Column("datetime") + uploadDate: Date | string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/repacker-friendly-name.entity.ts b/src/main/entity/repacker-friendly-name.entity.ts new file mode 100644 index 00000000..16b82e02 --- /dev/null +++ b/src/main/entity/repacker-friendly-name.entity.ts @@ -0,0 +1,25 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity("repacker_friendly_name") +export class RepackerFriendlyName { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { unique: true }) + name: string; + + @Column("text") + friendlyName: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/entity/steam-game.entity.ts b/src/main/entity/steam-game.entity.ts new file mode 100644 index 00000000..775372fb --- /dev/null +++ b/src/main/entity/steam-game.entity.ts @@ -0,0 +1,10 @@ +import { Column, Entity, PrimaryColumn } from "typeorm"; + +@Entity("steam_game") +export class SteamGame { + @PrimaryColumn() + id: number; + + @Column() + name: string; +} diff --git a/src/main/entity/user-preferences.entity.ts b/src/main/entity/user-preferences.entity.ts new file mode 100644 index 00000000..40f1a26a --- /dev/null +++ b/src/main/entity/user-preferences.entity.ts @@ -0,0 +1,34 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity("user_preferences") +export class UserPreferences { + @PrimaryGeneratedColumn() + id: number; + + @Column("text", { nullable: true }) + downloadsPath: string | null; + + @Column("text", { default: "en" }) + language: string; + + @Column("boolean", { default: false }) + downloadNotificationsEnabled: boolean; + + @Column("boolean", { default: false }) + repackUpdatesNotificationsEnabled: boolean; + + @Column("boolean", { default: true }) + telemetryEnabled: boolean; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/main/events/catalogue/get-catalogue.ts b/src/main/events/catalogue/get-catalogue.ts new file mode 100644 index 00000000..3e802c92 --- /dev/null +++ b/src/main/events/catalogue/get-catalogue.ts @@ -0,0 +1,117 @@ +import { formatName, getSteamAppAsset, repackerFormatter } from "@main/helpers"; +import type { CatalogueCategory, CatalogueEntry, GameShop } from "@types"; + +import { stateManager } from "@main/state-manager"; +import { searchGames, searchRepacks } from "../helpers/search-games"; +import { registerEvent } from "../register-event"; +import { requestSteam250 } from "@main/services"; + +const repacks = stateManager.getValue("repacks"); + +interface GetStringForLookup { + (index: number): string; +} + +const getCatalogue = async ( + _event: Electron.IpcMainInvokeEvent, + category: CatalogueCategory +) => { + const getStringForLookup = (index: number): string => { + const repack = repacks[index]; + const formatter = + repackerFormatter[repack.repacker as keyof typeof repackerFormatter]; + + return formatName(formatter(repack.title)); + }; + + if (!repacks.length) return []; + + const resultSize = 12; + + if (category === "trending") { + return getTrendingCatalogue(resultSize); + } else { + return getRecentlyAddedCatalogue( + resultSize, + resultSize, + getStringForLookup + ); + } +}; + +const getTrendingCatalogue = async ( + resultSize: number +): Promise => { + const results: CatalogueEntry[] = []; + const trendingGames = await requestSteam250("/30day"); + for ( + let i = 0; + i < trendingGames.length && results.length < resultSize; + i++ + ) { + if (!trendingGames[i]) continue; + + const { title, objectID } = trendingGames[i]; + const repacks = searchRepacks(title); + + if (title && repacks.length) { + const catalogueEntry = { + objectID, + title, + shop: "steam" as GameShop, + cover: getSteamAppAsset("library", objectID), + }; + + results.push({ ...catalogueEntry, repacks }); + } + } + return results; +}; + +const getRecentlyAddedCatalogue = async ( + resultSize: number, + requestSize: number, + getStringForLookup: GetStringForLookup +): Promise => { + let lookupRequest = []; + const results: CatalogueEntry[] = []; + + for (let i = 0; results.length < resultSize; i++) { + const stringForLookup = getStringForLookup(i); + + if (!stringForLookup) { + i++; + continue; + } + + lookupRequest.push(searchGames({ query: stringForLookup })); + + if (lookupRequest.length < requestSize) { + continue; + } + + const games = (await Promise.all(lookupRequest)).map((value) => + value.at(0) + ); + + for (const game of games) { + const isAlreadyIncluded = results.some( + (result) => result.objectID === game?.objectID + ); + + if (!game || !game.repacks.length || isAlreadyIncluded) { + continue; + } + + results.push(game); + } + lookupRequest = []; + } + + return results.slice(0, resultSize); +}; + +registerEvent(getCatalogue, { + name: "getCatalogue", + memoize: true, +}); diff --git a/src/main/events/catalogue/get-game-shop-details.ts b/src/main/events/catalogue/get-game-shop-details.ts new file mode 100644 index 00000000..61629242 --- /dev/null +++ b/src/main/events/catalogue/get-game-shop-details.ts @@ -0,0 +1,72 @@ +import { gameShopCacheRepository } from "@main/repository"; +import { getSteamAppDetails } from "@main/services"; + +import type { ShopDetails, GameShop, SteamAppDetails } from "@types"; + +import { registerEvent } from "../register-event"; +import { searchRepacks } from "../helpers/search-games"; + +const getGameShopDetails = async ( + _event: Electron.IpcMainInvokeEvent, + objectID: string, + shop: GameShop, + language: string +): Promise => { + if (shop === "steam") { + const cachedData = await gameShopCacheRepository.findOne({ + where: { objectID, language }, + }); + + const result = Promise.all([ + getSteamAppDetails(objectID, "english"), + getSteamAppDetails(objectID, language), + ]).then(([appDetails, localizedAppDetails]) => { + if (appDetails && localizedAppDetails) { + gameShopCacheRepository.upsert( + { + objectID, + shop: "steam", + language, + serializedData: JSON.stringify({ + ...localizedAppDetails, + name: appDetails.name, + }), + }, + ["objectID"] + ); + } + + return [appDetails, localizedAppDetails]; + }); + + const cachedGame = cachedData?.serializedData + ? (JSON.parse(cachedData?.serializedData) as SteamAppDetails) + : null; + + if (cachedGame) { + return { + ...cachedGame, + repacks: searchRepacks(cachedGame.name), + objectID, + } as ShopDetails; + } + + return result.then(([appDetails, localizedAppDetails]) => { + if (!appDetails || !localizedAppDetails) return null; + + return { + ...localizedAppDetails, + name: appDetails.name, + repacks: searchRepacks(appDetails.name), + objectID, + } as ShopDetails; + }); + } + + throw new Error("Not implemented"); +}; + +registerEvent(getGameShopDetails, { + name: "getGameShopDetails", + memoize: true, +}); diff --git a/src/main/events/catalogue/get-games.ts b/src/main/events/catalogue/get-games.ts new file mode 100644 index 00000000..75c6b62d --- /dev/null +++ b/src/main/events/catalogue/get-games.ts @@ -0,0 +1,44 @@ +import type { CatalogueEntry, GameShop } from "@types"; + +import { registerEvent } from "../register-event"; +import { searchRepacks } from "../helpers/search-games"; +import { stateManager } from "@main/state-manager"; +import { getSteamAppAsset } from "@main/helpers"; + +const steamGames = stateManager.getValue("steamGames"); + +const getGames = async ( + _event: Electron.IpcMainInvokeEvent, + take?: number, + cursor = 0 +): Promise<{ results: CatalogueEntry[]; cursor: number }> => { + const results: CatalogueEntry[] = []; + + let i = 0 + cursor; + + if (!steamGames.length) return []; + + while (results.length < take) { + const game = steamGames[i]; + const repacks = searchRepacks(game.name); + + if (repacks.length) { + results.push({ + objectID: String(game.id), + title: game.name, + shop: "steam" as GameShop, + cover: getSteamAppAsset("library", String(game.id)), + repacks, + }); + } + + i++; + } + + return { results, cursor: i }; +}; + +registerEvent(getGames, { + name: "getGames", + memoize: true, +}); diff --git a/src/main/events/catalogue/get-how-long-to-beat.ts b/src/main/events/catalogue/get-how-long-to-beat.ts new file mode 100644 index 00000000..601a3bf3 --- /dev/null +++ b/src/main/events/catalogue/get-how-long-to-beat.ts @@ -0,0 +1,48 @@ +import type { GameShop, HowLongToBeatCategory } from "@types"; +import { getHowLongToBeatGame, searchHowLongToBeat } from "@main/services"; + +import { registerEvent } from "../register-event"; +import { gameShopCacheRepository } from "@main/repository"; + +const getHowLongToBeat = async ( + _event: Electron.IpcMainInvokeEvent, + objectID: string, + shop: GameShop, + title: string +): Promise => { + const searchHowLongToBeatPromise = searchHowLongToBeat(title); + + const gameShopCache = await gameShopCacheRepository.findOne({ + where: { objectID, shop }, + }); + + const howLongToBeatCachedData = gameShopCache?.howLongToBeatSerializedData + ? JSON.parse(gameShopCache?.howLongToBeatSerializedData) + : null; + if (howLongToBeatCachedData) return howLongToBeatCachedData; + + return searchHowLongToBeatPromise.then(async (response) => { + const game = response.data.find( + (game) => game.profile_steam === Number(objectID) + ); + + if (!game) return null; + const howLongToBeat = await getHowLongToBeatGame(String(game.game_id)); + + gameShopCacheRepository.upsert( + { + objectID, + shop, + howLongToBeatSerializedData: JSON.stringify(howLongToBeat), + }, + ["objectID"] + ); + + return howLongToBeat; + }); +}; + +registerEvent(getHowLongToBeat, { + name: "getHowLongToBeat", + memoize: true, +}); diff --git a/src/main/events/catalogue/get-random-game.ts b/src/main/events/catalogue/get-random-game.ts new file mode 100644 index 00000000..335fa026 --- /dev/null +++ b/src/main/events/catalogue/get-random-game.ts @@ -0,0 +1,29 @@ +import { shuffle } from "lodash-es"; + +import { getRandomSteam250List } from "@main/services"; + +import { registerEvent } from "../register-event"; +import { searchGames, searchRepacks } from "../helpers/search-games"; +import { formatName } from "@main/helpers"; + +const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => { + return getRandomSteam250List().then(async (games) => { + const shuffledList = shuffle(games); + + for (const game of shuffledList) { + const repacks = searchRepacks(formatName(game.title)); + + if (repacks.length) { + const results = await searchGames({ query: game.title }); + + if (results.length) { + return results[0].objectID; + } + } + } + }); +}; + +registerEvent(getRandomGame, { + name: "getRandomGame", +}); diff --git a/src/main/events/catalogue/search-games.ts b/src/main/events/catalogue/search-games.ts new file mode 100644 index 00000000..eb9c0640 --- /dev/null +++ b/src/main/events/catalogue/search-games.ts @@ -0,0 +1,11 @@ +import { registerEvent } from "../register-event"; +import { searchGames } from "../helpers/search-games"; + +registerEvent( + (_event: Electron.IpcMainInvokeEvent, query: string) => + searchGames({ query, take: 12 }), + { + name: "searchGames", + memoize: true, + } +); diff --git a/src/main/events/hardware/get-disk-free-space.ts b/src/main/events/hardware/get-disk-free-space.ts new file mode 100644 index 00000000..28dcafa2 --- /dev/null +++ b/src/main/events/hardware/get-disk-free-space.ts @@ -0,0 +1,11 @@ +import checkDiskSpace from "check-disk-space"; + +import { registerEvent } from "../register-event"; +import { getDownloadsPath } from "../helpers/get-downloads-path"; + +const getDiskFreeSpace = async (_event: Electron.IpcMainInvokeEvent) => + checkDiskSpace(await getDownloadsPath()); + +registerEvent(getDiskFreeSpace, { + name: "getDiskFreeSpace", +}); diff --git a/src/main/events/helpers/generate-lutris-yaml.ts b/src/main/events/helpers/generate-lutris-yaml.ts new file mode 100644 index 00000000..75c9786b --- /dev/null +++ b/src/main/events/helpers/generate-lutris-yaml.ts @@ -0,0 +1,44 @@ +import { Document as YMLDocument } from "yaml"; +import { Game } from "@main/entity"; +import path from "node:path"; + +export const generateYML = (game: Game) => { + const slugifiedGameTitle = game.title.replace(/\s/g, "-").toLocaleLowerCase(); + + const doc = new YMLDocument({ + name: game.title, + game_slug: slugifiedGameTitle, + slug: `${slugifiedGameTitle}-installer`, + version: "Installer", + runner: "wine", + script: { + game: { + prefix: "$GAMEDIR", + arch: "win64", + working_dir: "$GAMEDIR", + }, + installer: [ + { + task: { + name: "create_prefix", + arch: "win64", + prefix: "$GAMEDIR", + }, + }, + { + task: { + executable: path.join( + game.downloadPath, + game.folderName, + "setup.exe" + ), + name: "wineexec", + prefix: "$GAMEDIR", + }, + }, + ], + }, + }); + + return doc.toString(); +}; diff --git a/src/main/events/helpers/get-downloads-path.ts b/src/main/events/helpers/get-downloads-path.ts new file mode 100644 index 00000000..c78a0ede --- /dev/null +++ b/src/main/events/helpers/get-downloads-path.ts @@ -0,0 +1,15 @@ +import { userPreferencesRepository } from "@main/repository"; +import { defaultDownloadsPath } from "@main/constants"; + +export const getDownloadsPath = async () => { + const userPreferences = await userPreferencesRepository.findOne({ + where: { + id: 1, + }, + }); + + if (userPreferences && userPreferences.downloadsPath) + return userPreferences.downloadsPath; + + return defaultDownloadsPath; +}; diff --git a/src/main/events/helpers/search-games.ts b/src/main/events/helpers/search-games.ts new file mode 100644 index 00000000..ddba13ba --- /dev/null +++ b/src/main/events/helpers/search-games.ts @@ -0,0 +1,71 @@ +import flexSearch from "flexsearch"; +import { orderBy } from "lodash-es"; + +import type { GameRepack, GameShop, CatalogueEntry } from "@types"; + +import { formatName, getSteamAppAsset, repackerFormatter } from "@main/helpers"; +import { stateManager } from "@main/state-manager"; + +const { Index } = flexSearch; +const repacksIndex = new Index(); +const steamGamesIndex = new Index({ tokenize: "forward" }); + +const repacks = stateManager.getValue("repacks"); +const steamGames = stateManager.getValue("steamGames"); + +for (let i = 0; i < repacks.length; i++) { + const repack = repacks[i]; + const formatter = + repackerFormatter[repack.repacker as keyof typeof repackerFormatter]; + + repacksIndex.add(i, formatName(formatter(repack.title))); +} + +for (let i = 0; i < steamGames.length; i++) { + const steamGame = steamGames[i]; + steamGamesIndex.add(i, formatName(steamGame.name)); +} + +export const searchRepacks = (title: string): GameRepack[] => { + return orderBy( + repacksIndex + .search(formatName(title)) + .map((index) => repacks.at(index as number)!), + ["uploadDate"], + "desc" + ); +}; + +export interface SearchGamesArgs { + query?: string; + take?: number; + skip?: number; +} + +export const searchGames = async ({ + query, + take, + skip, +}: SearchGamesArgs): Promise => { + const results = steamGamesIndex + .search(formatName(query || ""), { limit: take, offset: skip }) + .map((index) => { + const result = steamGames.at(index as number)!; + + return { + objectID: String(result.id), + title: result.name, + shop: "steam" as GameShop, + cover: getSteamAppAsset("library", String(result.id)), + repacks: searchRepacks(result.name), + }; + }); + + return Promise.all(results).then((resultsWithRepacks) => + orderBy( + resultsWithRepacks, + [({ repacks }) => repacks.length, "repacks"], + ["desc"] + ) + ); +}; diff --git a/src/main/events/index.ts b/src/main/events/index.ts new file mode 100644 index 00000000..5eaceff5 --- /dev/null +++ b/src/main/events/index.ts @@ -0,0 +1,32 @@ +import { defaultDownloadsPath } from "@main/constants"; +import { app, ipcMain } from "electron"; + +import "./catalogue/get-catalogue"; +import "./catalogue/get-game-shop-details"; +import "./catalogue/get-games"; +import "./catalogue/get-how-long-to-beat"; +import "./catalogue/get-random-game"; +import "./catalogue/search-games"; +import "./hardware/get-disk-free-space"; +import "./library/add-game-to-library"; +import "./library/close-game"; +import "./library/delete-game-folder"; +import "./library/get-game-by-object-id"; +import "./library/get-library"; +import "./library/get-repackers-friendly-names"; +import "./library/open-game"; +import "./library/open-game-installer"; +import "./library/remove-game"; +import "./misc/get-or-cache-image"; +import "./misc/open-external"; +import "./misc/show-open-dialog"; +import "./torrenting/cancel-game-download"; +import "./torrenting/pause-game-download"; +import "./torrenting/resume-game-download"; +import "./torrenting/start-game-download"; +import "./user-preferences/get-user-preferences"; +import "./user-preferences/update-user-preferences"; + +ipcMain.handle("ping", () => "pong"); +ipcMain.handle("getVersion", () => app.getVersion()); +ipcMain.handle("getDefaultDownloadsPath", () => defaultDownloadsPath); diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts new file mode 100644 index 00000000..abbba592 --- /dev/null +++ b/src/main/events/library/add-game-to-library.ts @@ -0,0 +1,29 @@ +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; + +import type { GameShop } from "@types"; +import { getImageBase64 } from "@main/helpers"; +import { getSteamGameIconUrl } from "@main/services"; + +const addGameToLibrary = async ( + _event: Electron.IpcMainInvokeEvent, + objectID: string, + title: string, + gameShop: GameShop, + executablePath: string +) => { + const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID)); + + return gameRepository.insert({ + title, + iconUrl, + objectID, + shop: gameShop, + executablePath, + }); +}; + +registerEvent(addGameToLibrary, { + name: "addGameToLibrary", +}); diff --git a/src/main/events/library/close-game.ts b/src/main/events/library/close-game.ts new file mode 100644 index 00000000..0d556925 --- /dev/null +++ b/src/main/events/library/close-game.ts @@ -0,0 +1,35 @@ +import path from "node:path"; + +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; +import { getProcesses } from "@main/helpers"; + +const closeGame = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const processes = await getProcesses(); + const game = await gameRepository.findOne({ where: { id: gameId } }); + + const gameProcess = processes.find((runningProcess) => { + const basename = path.win32.basename(game.executablePath); + const basenameWithoutExtension = path.win32.basename( + game.executablePath, + path.extname(game.executablePath) + ); + + if (process.platform === "win32") { + return runningProcess.name === basename; + } + + return [basename, basenameWithoutExtension].includes(runningProcess.name); + }); + + if (gameProcess) return process.kill(gameProcess.pid); + return false; +}; + +registerEvent(closeGame, { + name: "closeGame", +}); diff --git a/src/main/events/library/delete-game-folder.ts b/src/main/events/library/delete-game-folder.ts new file mode 100644 index 00000000..eaf791ed --- /dev/null +++ b/src/main/events/library/delete-game-folder.ts @@ -0,0 +1,47 @@ +import path from "node:path"; +import fs from "node:fs"; + +import { GameStatus } from "@main/constants"; +import { gameRepository } from "@main/repository"; + +import { getDownloadsPath } from "../helpers/get-downloads-path"; +import { logger } from "@main/services"; +import { registerEvent } from "../register-event"; + +const deleteGameFolder = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { + id: gameId, + status: GameStatus.Cancelled, + }, + }); + + if (!game) return; + + if (game.folderName) { + const folderPath = path.join(await getDownloadsPath(), game.folderName); + + if (fs.existsSync(folderPath)) { + return new Promise((resolve, reject) => { + fs.rm( + folderPath, + { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }, + (error) => { + if (error) { + logger.error(error); + reject(); + } + resolve(null); + } + ); + }); + } + } +}; + +registerEvent(deleteGameFolder, { + name: "deleteGameFolder", +}); diff --git a/src/main/events/library/get-game-by-object-id.ts b/src/main/events/library/get-game-by-object-id.ts new file mode 100644 index 00000000..522d3bbb --- /dev/null +++ b/src/main/events/library/get-game-by-object-id.ts @@ -0,0 +1,20 @@ +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; + +const getGameByObjectID = async ( + _event: Electron.IpcMainInvokeEvent, + objectID: string +) => + gameRepository.findOne({ + where: { + objectID, + }, + relations: { + repack: true, + }, + }); + +registerEvent(getGameByObjectID, { + name: "getGameByObjectID", +}); diff --git a/src/main/events/library/get-library.ts b/src/main/events/library/get-library.ts new file mode 100644 index 00000000..a38b0cc9 --- /dev/null +++ b/src/main/events/library/get-library.ts @@ -0,0 +1,30 @@ +import { gameRepository } from "@main/repository"; +import { GameStatus } from "@main/constants"; + +import { searchRepacks } from "../helpers/search-games"; +import { registerEvent } from "../register-event"; +import { sortBy } from "lodash-es"; + +const getLibrary = async (_event: Electron.IpcMainInvokeEvent) => + gameRepository + .find({ + order: { + createdAt: "desc", + }, + relations: { + repack: true, + }, + }) + .then((games) => + sortBy( + games.map((game) => ({ + ...game, + repacks: searchRepacks(game.title), + })), + (game) => (game.status !== GameStatus.Cancelled ? 0 : 1) + ) + ); + +registerEvent(getLibrary, { + name: "getLibrary", +}); diff --git a/src/main/events/library/get-repackers-friendly-names.ts b/src/main/events/library/get-repackers-friendly-names.ts new file mode 100644 index 00000000..22481d82 --- /dev/null +++ b/src/main/events/library/get-repackers-friendly-names.ts @@ -0,0 +1,12 @@ +import { registerEvent } from "../register-event"; +import { stateManager } from "@main/state-manager"; + +const getRepackersFriendlyNames = async (_event: Electron.IpcMainInvokeEvent) => + stateManager.getValue("repackersFriendlyNames").reduce((prev, next) => { + return { ...prev, [next.name]: next.friendlyName }; + }, {}); + +registerEvent(getRepackersFriendlyNames, { + name: "getRepackersFriendlyNames", + memoize: true, +}); diff --git a/src/main/events/library/open-game-installer.ts b/src/main/events/library/open-game-installer.ts new file mode 100644 index 00000000..acd3e343 --- /dev/null +++ b/src/main/events/library/open-game-installer.ts @@ -0,0 +1,58 @@ +import { gameRepository } from "@main/repository"; +import { generateYML } from "../helpers/generate-lutris-yaml"; +import path from "node:path"; +import fs from "node:fs"; +import { writeFile } from "node:fs/promises"; +import { spawnSync, exec } from "node:child_process"; + +import { registerEvent } from "../register-event"; +import { shell } from "electron"; +import { getDownloadsPath } from "../helpers/get-downloads-path"; + +const openGameInstaller = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ where: { id: gameId } }); + + if (!game) return true; + + const gamePath = path.join( + game.downloadPath ?? (await getDownloadsPath()), + game.folderName + ); + + if (!fs.existsSync(gamePath)) { + await gameRepository.delete({ id: gameId }); + return true; + } + + const setupPath = path.join(gamePath, "setup.exe"); + if (!fs.existsSync(setupPath)) { + shell.openPath(gamePath); + return true; + } + + if (process.platform === "win32") { + shell.openPath(setupPath); + return true; + } + + if (spawnSync("which", ["lutris"]).status === 0) { + const ymlPath = path.join(gamePath, "setup.yml"); + await writeFile(ymlPath, generateYML(game)); + exec(`lutris --install "${ymlPath}"`); + return true; + } + + if (spawnSync("which", ["wine"]).status === 0) { + exec(`wine "${setupPath}"`); + return true; + } + + return false; +}; + +registerEvent(openGameInstaller, { + name: "openGameInstaller", +}); diff --git a/src/main/events/library/open-game.ts b/src/main/events/library/open-game.ts new file mode 100644 index 00000000..2008e0d6 --- /dev/null +++ b/src/main/events/library/open-game.ts @@ -0,0 +1,18 @@ +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; +import { shell } from "electron"; + +const openGame = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number, + executablePath: string +) => { + await gameRepository.update({ id: gameId }, { executablePath }); + + shell.openPath(executablePath); +}; + +registerEvent(openGame, { + name: "openGame", +}); diff --git a/src/main/events/library/remove-game.ts b/src/main/events/library/remove-game.ts new file mode 100644 index 00000000..64e84b7d --- /dev/null +++ b/src/main/events/library/remove-game.ts @@ -0,0 +1,11 @@ +import { registerEvent } from "../register-event"; +import { gameRepository } from "../../repository"; + +const removeGame = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => gameRepository.delete({ id: gameId }); + +registerEvent(removeGame, { + name: "removeGame", +}); diff --git a/src/main/events/misc/get-or-cache-image.ts b/src/main/events/misc/get-or-cache-image.ts new file mode 100644 index 00000000..9734ff7e --- /dev/null +++ b/src/main/events/misc/get-or-cache-image.ts @@ -0,0 +1,37 @@ +import { imageCacheRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; +import { getImageBase64 } from "@main/helpers"; +import { logger } from "@main/services"; + +const getOrCacheImage = async ( + _event: Electron.IpcMainInvokeEvent, + url: string +) => { + const cache = await imageCacheRepository.findOne({ + where: { + url, + }, + }); + + if (cache) return cache.data; + + getImageBase64(url).then((data) => + imageCacheRepository + .save({ + url, + data, + }) + .catch(() => { + logger.error(`Failed to cache image "${url}"`, { + method: "getOrCacheImage", + }); + }) + ); + + return url; +}; + +registerEvent(getOrCacheImage, { + name: "getOrCacheImage", +}); diff --git a/src/main/events/misc/open-external.ts b/src/main/events/misc/open-external.ts new file mode 100644 index 00000000..3d693c23 --- /dev/null +++ b/src/main/events/misc/open-external.ts @@ -0,0 +1,9 @@ +import { shell } from "electron"; +import { registerEvent } from "../register-event"; + +const openExternal = async (_event: Electron.IpcMainInvokeEvent, src: string) => + shell.openExternal(src); + +registerEvent(openExternal, { + name: "openExternal", +}); diff --git a/src/main/events/misc/show-open-dialog.ts b/src/main/events/misc/show-open-dialog.ts new file mode 100644 index 00000000..123a47d6 --- /dev/null +++ b/src/main/events/misc/show-open-dialog.ts @@ -0,0 +1,12 @@ +import { dialog } from "electron"; +import { WindowManager } from "@main/services"; +import { registerEvent } from "../register-event"; + +const showOpenDialog = async ( + _event: Electron.IpcMainInvokeEvent, + options: Electron.OpenDialogOptions +) => dialog.showOpenDialog(WindowManager.mainWindow, options); + +registerEvent(showOpenDialog, { + name: "showOpenDialog", +}); diff --git a/src/main/events/register-event.ts b/src/main/events/register-event.ts new file mode 100644 index 00000000..60362329 --- /dev/null +++ b/src/main/events/register-event.ts @@ -0,0 +1,39 @@ +import { ipcMain } from "electron"; + +import { stateManager } from "@main/state-manager"; + +interface EventArgs { + name: string; + memoize?: boolean; +} + +export const registerEvent = ( + listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any, + { name, memoize = false }: EventArgs +) => { + ipcMain.handle(name, (event: Electron.IpcMainInvokeEvent, ...args) => { + const eventResults = stateManager.getValue("eventResults"); + const keys = Array.from(eventResults.keys()); + + const key = [name, args] as [string, any[]]; + + const memoizationKey = keys.find(([memoizedEvent, memoizedArgs]) => { + const sameEvent = name === memoizedEvent; + const sameArgs = memoizedArgs.every((arg, index) => arg === args[index]); + + return sameEvent && sameArgs; + }); + + if (memoizationKey) return eventResults.get(memoizationKey); + + return Promise.resolve(listener(event, ...args)).then((result) => { + if (memoize) { + eventResults.set(key, JSON.parse(JSON.stringify(result))); + stateManager.setValue("eventResults", eventResults); + } + + if (!result) return result; + return JSON.parse(JSON.stringify(result)); + }); + }); +}; diff --git a/src/main/events/torrenting/cancel-game-download.ts b/src/main/events/torrenting/cancel-game-download.ts new file mode 100644 index 00000000..686033cc --- /dev/null +++ b/src/main/events/torrenting/cancel-game-download.ts @@ -0,0 +1,53 @@ +import { GameStatus } from "@main/constants"; +import { gameRepository } from "@main/repository"; + +import { registerEvent } from "../register-event"; +import { WindowManager, writePipe } from "@main/services"; + +import { In } from "typeorm"; + +const cancelGameDownload = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { + id: gameId, + status: In([ + GameStatus.Downloading, + GameStatus.DownloadingMetadata, + GameStatus.CheckingFiles, + GameStatus.Paused, + GameStatus.Seeding, + ]), + }, + }); + + if (!game) return; + + gameRepository + .update( + { + id: game.id, + }, + { + status: GameStatus.Cancelled, + downloadPath: null, + bytesDownloaded: 0, + progress: 0, + } + ) + .then((result) => { + if ( + game.status !== GameStatus.Paused && + game.status !== GameStatus.Seeding + ) { + writePipe.write({ action: "cancel" }); + if (result.affected) WindowManager.mainWindow.setProgressBar(-1); + } + }); +}; + +registerEvent(cancelGameDownload, { + name: "cancelGameDownload", +}); diff --git a/src/main/events/torrenting/pause-game-download.ts b/src/main/events/torrenting/pause-game-download.ts new file mode 100644 index 00000000..d89f2f72 --- /dev/null +++ b/src/main/events/torrenting/pause-game-download.ts @@ -0,0 +1,34 @@ +import { WindowManager, writePipe } from "@main/services"; + +import { registerEvent } from "../register-event"; +import { GameStatus } from "../../constants"; +import { gameRepository } from "../../repository"; +import { In } from "typeorm"; + +const pauseGameDownload = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + await gameRepository + .update( + { + id: gameId, + status: In([ + GameStatus.Downloading, + GameStatus.DownloadingMetadata, + GameStatus.CheckingFiles, + ]), + }, + { status: GameStatus.Paused } + ) + .then((result) => { + if (result.affected) { + writePipe.write({ action: "pause" }); + WindowManager.mainWindow.setProgressBar(-1); + } + }); +}; + +registerEvent(pauseGameDownload, { + name: "pauseGameDownload", +}); diff --git a/src/main/events/torrenting/resume-game-download.ts b/src/main/events/torrenting/resume-game-download.ts new file mode 100644 index 00000000..c1e2e798 --- /dev/null +++ b/src/main/events/torrenting/resume-game-download.ts @@ -0,0 +1,56 @@ +import { registerEvent } from "../register-event"; +import { GameStatus } from "../../constants"; +import { gameRepository } from "../../repository"; +import { getDownloadsPath } from "../helpers/get-downloads-path"; +import { In } from "typeorm"; +import { writePipe } from "@main/services"; + +const resumeGameDownload = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { + id: gameId, + }, + relations: { repack: true }, + }); + + if (!game) return; + + writePipe.write({ action: "pause" }); + + if (game.status === GameStatus.Paused) { + const downloadsPath = game.downloadPath ?? (await getDownloadsPath()); + + writePipe.write({ + action: "start", + game_id: gameId, + magnet: game.repack.magnet, + save_path: downloadsPath, + }); + + await gameRepository.update( + { + status: In([ + GameStatus.Downloading, + GameStatus.DownloadingMetadata, + GameStatus.CheckingFiles, + ]), + }, + { status: GameStatus.Paused } + ); + + await gameRepository.update( + { id: game.id }, + { + status: GameStatus.DownloadingMetadata, + downloadPath: downloadsPath, + } + ); + } +}; + +registerEvent(resumeGameDownload, { + name: "resumeGameDownload", +}); diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts new file mode 100644 index 00000000..570fd2ec --- /dev/null +++ b/src/main/events/torrenting/start-game-download.ts @@ -0,0 +1,110 @@ +import { getSteamGameIconUrl, writePipe } from "@main/services"; +import { gameRepository, repackRepository } from "@main/repository"; +import { GameStatus } from "@main/constants"; + +import { registerEvent } from "../register-event"; + +import type { GameShop } from "@types"; +import { getDownloadsPath } from "../helpers/get-downloads-path"; +import { getImageBase64 } from "@main/helpers"; +import { In } from "typeorm"; + +const startGameDownload = async ( + _event: Electron.IpcMainInvokeEvent, + repackId: number, + objectID: string, + title: string, + gameShop: GameShop +) => { + const [game, repack] = await Promise.all([ + gameRepository.findOne({ + where: { + objectID, + }, + }), + repackRepository.findOne({ + where: { + id: repackId, + }, + }), + ]); + + if (!repack) return; + + if (game?.status === GameStatus.Downloading) { + return; + } + + writePipe.write({ action: "pause" }); + + const downloadsPath = game?.downloadPath ?? (await getDownloadsPath()); + + await gameRepository.update( + { + status: In([ + GameStatus.Downloading, + GameStatus.DownloadingMetadata, + GameStatus.CheckingFiles, + ]), + }, + { status: GameStatus.Paused } + ); + + if (game) { + await gameRepository.update( + { + id: game.id, + }, + { + status: GameStatus.DownloadingMetadata, + downloadPath: downloadsPath, + repack: { id: repackId }, + } + ); + + writePipe.write({ + action: "start", + game_id: game.id, + magnet: repack.magnet, + save_path: downloadsPath, + }); + + game.status = GameStatus.DownloadingMetadata; + + writePipe.write({ + action: "start", + game_id: game.id, + magnet: repack.magnet, + save_path: downloadsPath, + }); + + return game; + } else { + const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID)); + + const createdGame = await gameRepository.save({ + title, + iconUrl, + objectID, + shop: gameShop, + status: GameStatus.DownloadingMetadata, + downloadPath: downloadsPath, + repack: { id: repackId }, + }); + + writePipe.write({ + action: "start", + game_id: createdGame.id, + magnet: repack.magnet, + save_path: downloadsPath, + }); + + const { repack: _, ...rest } = createdGame; + + return rest; + } +}; + +registerEvent(startGameDownload, { + name: "startGameDownload", +}); diff --git a/src/main/events/user-preferences/get-user-preferences.ts b/src/main/events/user-preferences/get-user-preferences.ts new file mode 100644 index 00000000..219713eb --- /dev/null +++ b/src/main/events/user-preferences/get-user-preferences.ts @@ -0,0 +1,11 @@ +import { userPreferencesRepository } from "@main/repository"; +import { registerEvent } from "../register-event"; + +const getUserPreferences = async (_event: Electron.IpcMainInvokeEvent) => + userPreferencesRepository.findOne({ + where: { id: 1 }, + }); + +registerEvent(getUserPreferences, { + name: "getUserPreferences", +}); diff --git a/src/main/events/user-preferences/update-user-preferences.ts b/src/main/events/user-preferences/update-user-preferences.ts new file mode 100644 index 00000000..000eca7b --- /dev/null +++ b/src/main/events/user-preferences/update-user-preferences.ts @@ -0,0 +1,21 @@ +import { userPreferencesRepository } from "@main/repository"; +import { registerEvent } from "../register-event"; + +import type { UserPreferences } from "@types"; + +const updateUserPreferences = async ( + _event: Electron.IpcMainInvokeEvent, + preferences: Partial +) => { + await userPreferencesRepository.upsert( + { + id: 1, + ...preferences, + }, + ["id"] + ); +}; + +registerEvent(updateUserPreferences, { + name: "updateUserPreferences", +}); diff --git a/src/main/helpers/formatters.test.ts b/src/main/helpers/formatters.test.ts new file mode 100644 index 00000000..6b0d0fae --- /dev/null +++ b/src/main/helpers/formatters.test.ts @@ -0,0 +1,98 @@ +import assert from "node:assert/strict"; +import { describe, test } from "node:test"; +import { + dodiFormatter, + empressFormatter, + fitGirlFormatter, + kaosKrewFormatter, +} from "./formatters"; + +describe("testing formatters", () => { + describe("testing fitgirl formatter", () => { + const fitGirlGames = [ + "REVEIL (v1.0.3f4 + 0.5 DLC, MULTi14) [FitGirl Repack]", + "Dune: Spice Wars - The Ixian Edition (v2.0.0.31558 + DLC, MULTi9) [FitGirl Repack]", + "HUMANKIND: Premium Edition (v1.0.22.3819 + 17 DLCs/Bonus Content, MULTi12) [FitGirl Repack, Selective Download - from 7.3 GB]", + "Call to Arms: Gates of Hell - Ostfront: WW2 Bundle (v1.034 Hotfix 3 + 3 DLCs, MULTi9) [FitGirl Repack, Selective Download - from 21.8 GB]", + "SUPER BOMBERMAN R 2 (v1.2.0, MULTi12) [FitGirl Repack]", + "God of Rock (v3110, MULTi11) [FitGirl Repack]", + ]; + + test("should format games correctly", () => { + assert.equal(fitGirlGames.map(fitGirlFormatter), [ + "REVEIL", + "Dune: Spice Wars - The Ixian Edition", + "HUMANKIND: Premium Edition", + "Call to Arms: Gates of Hell - Ostfront: WW2 Bundle", + "SUPER BOMBERMAN R 2", + "God of Rock", + ]); + }); + }); + + describe("testing kaoskrew formatter", () => { + const kaosKrewGames = [ + "Song.Of.Horror.Complete.Edition.v1.25.MULTi4.REPACK-KaOs", + "Remoteness.REPACK-KaOs", + "Persona.5.Royal.v1.0.0.MULTi5.NSW.For.PC.REPACK-KaOs", + "The.Wreck.MULTi5.REPACK-KaOs", + "Nemezis.Mysterious.Journey.III.v1.04.Deluxe.Edition.REPACK-KaOs", + "The.World.Of.Others.v1.05.REPACK-KaOs", + ]; + + test("should format games correctly", () => { + assert.equal(kaosKrewGames.map(kaosKrewFormatter), [ + "Song Of Horror Complete Edition", + "Remoteness", + "Persona 5 Royal NSW For PC", + "The Wreck", + "Nemezis Mysterious Journey III Deluxe Edition", + "The World Of Others", + ]); + }); + }); + + describe("testing empress formatter", () => { + const empressGames = [ + "Resident.Evil.4-EMPRESS", + "Marvels.Guardians.of.the.Galaxy.Crackfix-EMPRESS", + "Life.is.Strange.2.Complete.Edition-EMPRESS", + "Forza.Horizon.4.PROPER-EMPRESS", + "Just.Cause.4.Complete.Edition.READNFO-EMPRESS", + "Immortals.Fenyx.Rising.Crackfix.V2-EMPRESS", + ]; + + test("should format games correctly", () => { + assert.equal(empressGames.map(empressFormatter), [ + "Resident Evil 4", + "Marvels Guardians of the Galaxy", + "Life is Strange 2 Complete Edition", + "Forza Horizon 4 PROPER", + "Just Cause 4 Complete Edition", + "Immortals Fenyx Rising", + ]); + }); + }); + + describe("testing kodi formatter", () => { + const dodiGames = [ + "Tomb Raider I-III Remastered Starring Lara Croft (MULTi20) (From 2.5 GB) [DODI Repack]", + "Trail Out: Complete Edition (v2.9st + All DLCs + MULTi11) [DODI Repack]", + "Call to Arms - Gates of Hell: Ostfront (v1.034.0 + All DLCs + MULTi9) (From 22.4 GB) [DODI Repack]", + "Metal Gear Solid 2: Sons of Liberty - HD Master Collection Edition (Digital book + MULTi6) [DODI Repack]", + "DREDGE: Digital Deluxe Edition (v1.2.0.1922 + All DLCs + Bonus Content + MULTi11) (From 413 MB) [DODI Repack]", + "Outliver: Tribulation [DODI Repack]", + ]; + + test("should format games correctly", () => { + assert.equal(dodiGames.map(dodiFormatter), [ + "Tomb Raider I-III Remastered Starring Lara Croft", + "Trail Out: Complete Edition", + "Call to Arms - Gates of Hell: Ostfront", + "Metal Gear Solid 2: Sons of Liberty - HD Master Collection Edition", + "DREDGE: Digital Deluxe Edition", + "Outliver: Tribulation", + ]); + }); + }); +}); diff --git a/src/main/helpers/formatters.ts b/src/main/helpers/formatters.ts new file mode 100644 index 00000000..6ad67625 --- /dev/null +++ b/src/main/helpers/formatters.ts @@ -0,0 +1,56 @@ +/* String formatting */ + +export const removeReleaseYearFromName = (name: string) => + name.replace(/\([0-9]{4}\)/g, ""); + +export const removeSymbolsFromName = (name: string) => + name.replace(/[^A-Za-z 0-9]/g, ""); + +export const removeSpecialEditionFromName = (name: string) => + name.replace( + /(The |Digital )?(GOTY|Deluxe|Standard|Ultimate|Definitive|Enhanced|Collector's|Premium|Digital|Limited|Game of the Year|Reloaded|[0-9]{4}) Edition/g, + "" + ); + +export const removeDuplicateSpaces = (name: string) => + name.replace(/\s{2,}/g, " "); + +export const removeTrash = (title: string) => + title.replace(/\(.*\)|\[.*]/g, "").replace(/:/g, ""); + +/* Formatters per repacker */ + +export const fitGirlFormatter = (title: string) => + title.replace(/\(.*\)/g, "").trim(); + +export const kaosKrewFormatter = (title: string) => + title + .replace(/(v\.?[0-9])+([0-9]|\.)+/, "") + .replace( + /(\.Build\.[0-9]*)?(\.MULTi[0-9]{1,2})?(\.REPACK-KaOs|\.UPDATE-KaOs)?/g, + "" + ) + .replace(/\./g, " ") + .trim(); + +export const empressFormatter = (title: string) => + title + .replace(/-EMPRESS/, "") + .replace(/\./g, " ") + .trim(); + +export const dodiFormatter = (title: string) => + title.replace(/\(.*?\)/g, "").trim(); + +export const xatabFormatter = (title: string) => + title + .replace(/RePack от xatab|RePack от Decepticon|R.G. GOGFAN/, "") + .replace(/[\u0400-\u04FF]/g, "") + .replace(/(v\.?([0-9]| )+)+([0-9]|\.|-|_|\/|[a-zA-Z]| )+/, ""); + +export const tinyRepacksFormatter = (title: string) => title; +export const onlinefixFormatter = (title: string) => + title.replace("по сети", "").trim(); + +export const gogFormatter = (title: string) => + title.replace(/(v\.[0-9]+|v[0-9]+\.|v[0-9]{4})+.+/, ""); diff --git a/src/main/helpers/index.ts b/src/main/helpers/index.ts new file mode 100644 index 00000000..93b79229 --- /dev/null +++ b/src/main/helpers/index.ts @@ -0,0 +1,85 @@ +import { + removeReleaseYearFromName, + removeSymbolsFromName, + removeSpecialEditionFromName, + empressFormatter, + kaosKrewFormatter, + fitGirlFormatter, + removeDuplicateSpaces, + dodiFormatter, + removeTrash, + xatabFormatter, + tinyRepacksFormatter, + gogFormatter, + onlinefixFormatter, +} from "./formatters"; +import { months, repackers } from "../constants"; + +export const pipe = + (...fns: ((arg: T) => any)[]) => + (arg: T) => + fns.reduce((prev, fn) => fn(prev), arg); + +export const formatName = pipe( + removeTrash, + removeReleaseYearFromName, + removeSymbolsFromName, + removeSpecialEditionFromName, + removeDuplicateSpaces, + (str) => str.trim() +); + +export const repackerFormatter: Record< + (typeof repackers)[number], + (title: string) => string +> = { + DODI: dodiFormatter, + "0xEMPRESS": empressFormatter, + KaOsKrew: kaosKrewFormatter, + FitGirl: fitGirlFormatter, + Xatab: xatabFormatter, + CPG: (title: string) => title, + TinyRepacks: tinyRepacksFormatter, + GOG: gogFormatter, + onlinefix: onlinefixFormatter, +}; + +export const formatUploadDate = (str: string) => { + const date = new Date(); + + const [month, day, year] = str.split(" "); + + date.setMonth(months.indexOf(month.replace(".", ""))); + date.setDate(Number(day.substring(0, 2))); + date.setFullYear(Number("20" + year.replace("'", ""))); + date.setHours(0, 0, 0, 0); + + return date; +}; + +export const getSteamAppAsset = ( + category: "library" | "hero" | "logo" | "icon", + objectID: string, + clientIcon?: string +) => { + if (category === "library") + return `https://steamcdn-a.akamaihd.net/steam/apps/${objectID}/header.jpg`; + + if (category === "hero") + return `https://steamcdn-a.akamaihd.net/steam/apps/${objectID}/library_hero.jpg`; + + if (category === "logo") + return `https://cdn.cloudflare.steamstatic.com/steam/apps/${objectID}/logo.png`; + + return `https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/${objectID}/${clientIcon}.ico`; +}; + +export const getImageBase64 = async (url: string) => + fetch(url, { method: "GET" }).then((response) => + response.arrayBuffer().then((buffer) => { + return `data:image/jpeg;base64,${Buffer.from(buffer).toString("base64")}`; + }) + ); + +export * from "./formatters"; +export * from "./ps"; diff --git a/src/main/helpers/ps.ts b/src/main/helpers/ps.ts new file mode 100644 index 00000000..f7ce3391 --- /dev/null +++ b/src/main/helpers/ps.ts @@ -0,0 +1,12 @@ +import psList from "ps-list"; +import { tasklist } from "tasklist"; + +export const getProcesses = async () => { + if (process.platform === "win32") { + return tasklist().then((tasks) => + tasks.map((task) => ({ ...task, name: task.imageName })) + ); + } + + return psList(); +}; diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 00000000..a48db177 --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,102 @@ +import { app, BrowserWindow } from "electron"; +import { init } from "@sentry/electron/main"; +import i18n from "i18next"; +import path from "node:path"; +import { resolveDatabaseUpdates, WindowManager } from "@main/services"; +import { dataSource } from "@main/data-source"; +import * as resources from "@locales"; +import { userPreferencesRepository } from "@main/repository"; + +const gotTheLock = app.requestSingleInstanceLock(); +if (!gotTheLock) app.quit(); + +if (import.meta.env.MAIN_VITE_SENTRY_DSN) { + init({ + dsn: import.meta.env.MAIN_VITE_SENTRY_DSN, + beforeSend: async (event) => { + const userPreferences = await userPreferencesRepository.findOne({ + where: { id: 1 }, + }); + + if (userPreferences?.telemetryEnabled) return event; + return null; + }, + }); +} + +i18n.init({ + resources, + lng: "en", + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, +}); + +const PROTOCOL = "hydralauncher"; + +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [ + path.resolve(process.argv[1]), + ]); + } +} else { + app.setAsDefaultProtocolClient(PROTOCOL); +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.whenReady().then(() => { + dataSource.initialize().then(async () => { + // await resolveDatabaseUpdates(); + + await import("./main"); + + const userPreferences = await userPreferencesRepository.findOne({ + where: { id: 1 }, + }); + + WindowManager.createMainWindow(); + // WindowManager.createSystemTray(userPreferences?.language || "en"); + }); +}); + +app.on("second-instance", (_event, commandLine) => { + // Someone tried to run a second instance, we should focus our window. + if (WindowManager.mainWindow) { + if (WindowManager.mainWindow.isMinimized()) + WindowManager.mainWindow.restore(); + + WindowManager.mainWindow.focus(); + } else { + WindowManager.createMainWindow(); + } + + const [, path] = commandLine.pop().split("://"); + if (path) WindowManager.redirect(path); +}); + +app.on("open-url", (_event, url) => { + const [, path] = url.split("://"); + WindowManager.redirect(path); +}); + +// Quit when all windows are closed, except on macOS. There, it's common +// for applications and their menu bar to stay active until the user quits +// explicitly with Cmd + Q. +app.on("window-all-closed", () => { + WindowManager.mainWindow = null; +}); + +app.on("activate", () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) { + WindowManager.createMainWindow(); + } +}); + +// In this file you can include the rest of your app's specific main process +// code. You can also put them in separate files and import them here. diff --git a/src/main/main.ts b/src/main/main.ts new file mode 100644 index 00000000..5637bbd3 --- /dev/null +++ b/src/main/main.ts @@ -0,0 +1,129 @@ +import { stateManager } from "./state-manager"; +import { GameStatus, repackers } from "./constants"; +import { + getNewGOGGames, + getNewRepacksFromCPG, + getNewRepacksFromUser, + getNewRepacksFromXatab, + // getNewRepacksFromOnlineFix, + readPipe, + startProcessWatcher, + writePipe, +} from "./services"; +import { + gameRepository, + repackRepository, + repackerFriendlyNameRepository, + steamGameRepository, + userPreferencesRepository, +} from "./repository"; +import { TorrentClient } from "./services/torrent-client"; +import { Repack } from "./entity"; +import { Notification } from "electron"; +import { t } from "i18next"; +import { In } from "typeorm"; + +startProcessWatcher(); + +TorrentClient.startTorrentClient(writePipe.socketPath, readPipe.socketPath); + +Promise.all([writePipe.createPipe(), readPipe.createPipe()]).then(async () => { + const game = await gameRepository.findOne({ + where: { + status: In([ + GameStatus.Downloading, + GameStatus.DownloadingMetadata, + GameStatus.CheckingFiles, + ]), + }, + relations: { repack: true }, + }); + + if (game) { + writePipe.write({ + action: "start", + game_id: game.id, + magnet: game.repack.magnet, + save_path: game.downloadPath, + }); + } + + readPipe.socket.on("data", (data) => { + TorrentClient.onSocketData(data); + }); +}); + +const track1337xUsers = async (existingRepacks: Repack[]) => { + for (const repacker of repackers) { + await getNewRepacksFromUser( + repacker, + existingRepacks.filter((repack) => repack.repacker === repacker) + ); + } +}; + +const checkForNewRepacks = async () => { + const userPreferences = await userPreferencesRepository.findOne({ + where: { id: 1 }, + }); + + const existingRepacks = stateManager.getValue("repacks"); + + Promise.allSettled([ + getNewGOGGames( + existingRepacks.filter((repack) => repack.repacker === "GOG") + ), + getNewRepacksFromXatab( + existingRepacks.filter((repack) => repack.repacker === "Xatab") + ), + getNewRepacksFromCPG( + existingRepacks.filter((repack) => repack.repacker === "CPG") + ), + // getNewRepacksFromOnlineFix( + // existingRepacks.filter((repack) => repack.repacker === "onlinefix") + // ), + track1337xUsers(existingRepacks), + ]).then(() => { + repackRepository.count().then((count) => { + const total = count - stateManager.getValue("repacks").length; + + if (total > 0 && userPreferences?.repackUpdatesNotificationsEnabled) { + new Notification({ + title: t("repack_list_updated", { + ns: "notifications", + lng: userPreferences?.language || "en", + }), + body: t("repack_count", { + ns: "notifications", + lng: userPreferences?.language || "en", + count: total, + }), + }).show(); + } + }); + }); +}; + +const loadState = async () => { + const [friendlyNames, repacks, steamGames] = await Promise.all([ + repackerFriendlyNameRepository.find(), + repackRepository.find({ + order: { + createdAt: "desc", + }, + }), + steamGameRepository.find({ + order: { + name: "asc", + }, + }), + ]); + + stateManager.setValue("repackersFriendlyNames", friendlyNames); + stateManager.setValue("repacks", repacks); + stateManager.setValue("steamGames", steamGames); + + import("./events"); +}; + +loadState().then(() => checkForNewRepacks()); diff --git a/src/main/repository.ts b/src/main/repository.ts new file mode 100644 index 00000000..f9edfa1c --- /dev/null +++ b/src/main/repository.ts @@ -0,0 +1,30 @@ +import { dataSource } from "./data-source"; +import { + Game, + GameShopCache, + ImageCache, + Repack, + RepackerFriendlyName, + UserPreferences, + MigrationScript, + SteamGame, +} from "@main/entity"; + +export const gameRepository = dataSource.getRepository(Game); + +export const imageCacheRepository = dataSource.getRepository(ImageCache); + +export const repackRepository = dataSource.getRepository(Repack); + +export const repackerFriendlyNameRepository = + dataSource.getRepository(RepackerFriendlyName); + +export const userPreferencesRepository = + dataSource.getRepository(UserPreferences); + +export const gameShopCacheRepository = dataSource.getRepository(GameShopCache); + +export const migrationScriptRepository = + dataSource.getRepository(MigrationScript); + +export const steamGameRepository = dataSource.getRepository(SteamGame); diff --git a/src/main/services/fifo.ts b/src/main/services/fifo.ts new file mode 100644 index 00000000..866232cc --- /dev/null +++ b/src/main/services/fifo.ts @@ -0,0 +1,38 @@ +import path from "node:path"; +import net from "node:net"; +import crypto from "node:crypto"; +import os from "node:os"; + +export class FIFO { + public socket: null | net.Socket = null; + public socketPath = this.generateSocketFilename(); + + private generateSocketFilename() { + const hash = crypto.randomBytes(16).toString("hex"); + + if (process.platform === "win32") { + return "\\\\.\\pipe\\" + hash; + } + + return path.join(os.tmpdir(), hash); + } + + public write(data: any) { + if (!this.socket) return; + this.socket.write(Buffer.from(JSON.stringify(data))); + } + + public createPipe() { + return new Promise((resolve) => { + const server = net.createServer((socket) => { + this.socket = socket; + resolve(null); + }); + + server.listen(this.socketPath); + }); + } +} + +export const writePipe = new FIFO(); +export const readPipe = new FIFO(); diff --git a/src/main/services/how-long-to-beat.ts b/src/main/services/how-long-to-beat.ts new file mode 100644 index 00000000..76d53c6d --- /dev/null +++ b/src/main/services/how-long-to-beat.ts @@ -0,0 +1,60 @@ +import { formatName } from "@main/helpers"; +import axios from "axios"; +import { JSDOM } from "jsdom"; +import { requestWebPage } from "./repack-tracker/helpers"; +import { HowLongToBeatCategory } from "@types"; + +export interface HowLongToBeatResult { + game_id: number; + profile_steam: number; +} + +export interface HowLongToBeatSearchResponse { + data: HowLongToBeatResult[]; +} + +export const searchHowLongToBeat = async (gameName: string) => { + const response = await axios.post( + "https://howlongtobeat.com/api/search", + { + searchType: "games", + searchTerms: formatName(gameName).split(" "), + searchPage: 1, + size: 100, + }, + { + headers: { + "User-Agent": + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", + Referer: "https://howlongtobeat.com/", + }, + } + ); + + return response.data as HowLongToBeatSearchResponse; +}; + +export const getHowLongToBeatGame = async ( + id: string +): Promise => { + const response = await requestWebPage(`https://howlongtobeat.com/game/${id}`); + + const { window } = new JSDOM(response); + const { document } = window; + + const $ul = document.querySelector(".shadow_shadow ul"); + const $lis = Array.from($ul.children); + + return $lis.map(($li) => { + const title = $li.querySelector("h4").textContent; + const [, accuracyClassName] = Array.from(($li as HTMLElement).classList); + + const accuracy = accuracyClassName.split("time_").at(1); + + return { + title, + duration: $li.querySelector("h5").textContent, + accuracy, + }; + }); +}; diff --git a/src/main/services/index.ts b/src/main/services/index.ts new file mode 100644 index 00000000..2544c6f4 --- /dev/null +++ b/src/main/services/index.ts @@ -0,0 +1,11 @@ +export * from "./logger"; +export * from "./repack-tracker"; +export * from "./steam"; +export * from "./steam-250"; +export * from "./steam-grid"; +export * from "./update-resolver"; +export * from "./window-manager"; +export * from "./fifo"; +export * from "./torrent-client"; +export * from "./how-long-to-beat"; +export * from "./process-watcher"; diff --git a/src/main/services/logger.ts b/src/main/services/logger.ts new file mode 100644 index 00000000..07a5d153 --- /dev/null +++ b/src/main/services/logger.ts @@ -0,0 +1,11 @@ +import winston from "winston"; + +export const logger = winston.createLogger({ + level: "info", + format: winston.format.json(), + transports: [ + new winston.transports.File({ filename: "error.log", level: "error" }), + new winston.transports.File({ filename: "info.log", level: "info" }), + new winston.transports.File({ filename: "combined.log" }), + ], +}); diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts new file mode 100644 index 00000000..96244a60 --- /dev/null +++ b/src/main/services/process-watcher.ts @@ -0,0 +1,80 @@ +import path from "node:path"; + +import { IsNull, Not } from "typeorm"; + +import { gameRepository } from "@main/repository"; +import { getProcesses } from "@main/helpers"; +import { WindowManager } from "./window-manager"; + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const startProcessWatcher = async () => { + const sleepTime = 100; + const gamesPlaytime = new Map(); + + // eslint-disable-next-line no-constant-condition + while (true) { + const games = await gameRepository.find({ + where: { + executablePath: Not(IsNull()), + }, + }); + + const processes = await getProcesses(); + + for (const game of games) { + const gameProcess = processes.find((runningProcess) => { + const basename = path.win32.basename(game.executablePath); + const basenameWithoutExtension = path.win32.basename( + game.executablePath, + path.extname(game.executablePath) + ); + + if (process.platform === "win32") { + return runningProcess.name === basename; + } + + return [basename, basenameWithoutExtension].includes( + runningProcess.name + ); + }); + + if (gameProcess) { + if (gamesPlaytime.has(game.id)) { + const zero = gamesPlaytime.get(game.id); + const delta = performance.now() - zero; + + if (WindowManager.mainWindow) { + WindowManager.mainWindow.webContents.send("on-playtime", game.id); + } + + await gameRepository.update(game.id, { + playTimeInMilliseconds: game.playTimeInMilliseconds + delta, + }); + + gameRepository.update(game.id, { + lastTimePlayed: new Date().toUTCString(), + }); + + gamesPlaytime.set(game.id, performance.now()); + await sleep(sleepTime); + continue; + } + + gamesPlaytime.set(game.id, performance.now()); + + await sleep(sleepTime); + continue; + } + + if (gamesPlaytime.has(game.id)) { + gamesPlaytime.delete(game.id); + if (WindowManager.mainWindow) { + WindowManager.mainWindow.webContents.send("on-game-close", game.id); + } + } + + await sleep(sleepTime); + } + } +}; diff --git a/src/main/services/repack-tracker/1337x.ts b/src/main/services/repack-tracker/1337x.ts new file mode 100644 index 00000000..1cbafa3b --- /dev/null +++ b/src/main/services/repack-tracker/1337x.ts @@ -0,0 +1,135 @@ +import { JSDOM } from "jsdom"; + +import { formatUploadDate } from "@main/helpers"; + +import { Repack } from "@main/entity"; +import { requestWebPage, savePage } from "./helpers"; +import type { GameRepackInput } from "./helpers"; + +export const request1337x = async (path: string) => + requestWebPage(`https://1337xx.to${path}`); + +/* TODO: $a will often be null */ +const getTorrentDetails = async (path: string) => { + const response = await request1337x(path); + + const { window } = new JSDOM(response); + const { document } = window; + + const $a = window.document.querySelector( + ".torrentdown1" + ) as HTMLAnchorElement; + + const $ul = Array.from( + document.querySelectorAll(".torrent-detail-page .list") + ); + const [$firstColumn, $secondColumn] = $ul; + + if (!$firstColumn || !$secondColumn) { + return { magnet: $a?.href }; + } + + const [_$category, _$type, _$language, $totalSize] = $firstColumn.children; + const [_$downloads, _$lastChecked, $dateUploaded] = $secondColumn.children; + + return { + magnet: $a?.href, + fileSize: $totalSize.querySelector("span").textContent ?? undefined, + uploadDate: formatUploadDate( + $dateUploaded.querySelector("span").textContent! + ), + }; +}; + +export const getTorrentListLastPage = async (user: string) => { + const response = await request1337x(`/user/${user}/1`); + + const { window } = new JSDOM(response); + + const $ul = window.document.querySelector(".pagination > ul"); + + if ($ul) { + const $li = Array.from($ul.querySelectorAll("li")).at(-1); + const text = $li?.textContent; + + if (text === ">>") { + const $previousLi = Array.from($ul.querySelectorAll("li")).at(-2); + return Number($previousLi?.textContent); + } + + return Number(text); + } + + return -1; +}; + +export const extractTorrentsFromDocument = async ( + page: number, + user: string, + document: Document, + existingRepacks: Repack[] = [] +): Promise => { + const $trs = Array.from(document.querySelectorAll("tbody tr")); + + return Promise.all( + $trs.map(async ($tr) => { + const $td = $tr.querySelector("td"); + + const [, $name] = Array.from($td!.querySelectorAll("a")); + const url = $name.href; + const title = $name.textContent ?? ""; + + if (existingRepacks.some((repack) => repack.title === title)) { + return { + title, + magnet: "", + fileSize: null, + uploadDate: null, + repacker: user, + page, + }; + } + + const details = await getTorrentDetails(url); + + return { + title, + magnet: details.magnet, + fileSize: details.fileSize ?? null, + uploadDate: details.uploadDate ?? null, + repacker: user, + page, + }; + }) + ); +}; + +export const getNewRepacksFromUser = async ( + user: string, + existingRepacks: Repack[], + page = 1 +): Promise => { + const response = await request1337x(`/user/${user}/${page}`); + const { window } = new JSDOM(response); + + const repacks = await extractTorrentsFromDocument( + page, + user, + window.document, + existingRepacks + ); + + const newRepacks = repacks.filter( + (repack) => + repack.uploadDate && + !existingRepacks.some( + (existingRepack) => existingRepack.title === repack.title + ) + ); + + if (!newRepacks.length) return; + + await savePage(newRepacks); + + return getNewRepacksFromUser(user, existingRepacks, page + 1); +}; diff --git a/src/main/services/repack-tracker/cpg-repacks.ts b/src/main/services/repack-tracker/cpg-repacks.ts new file mode 100644 index 00000000..0d7c172b --- /dev/null +++ b/src/main/services/repack-tracker/cpg-repacks.ts @@ -0,0 +1,65 @@ +import { JSDOM } from "jsdom"; + +import { Repack } from "@main/entity"; + +import { requestWebPage, savePage } from "./helpers"; +import type { GameRepackInput } from "./helpers"; +import { logger } from "../logger"; + +export const getNewRepacksFromCPG = async ( + existingRepacks: Repack[] = [], + page = 1 +): Promise => { + const data = await requestWebPage(`https://cpgrepacks.site/page/${page}`); + + const { window } = new JSDOM(data); + + const repacks: GameRepackInput[] = []; + + try { + Array.from(window.document.querySelectorAll(".post")).forEach(($post) => { + const $title = $post.querySelector(".entry-title"); + const uploadDate = $post.querySelector("time").getAttribute("datetime"); + + const $downloadInfo = Array.from( + $post.querySelectorAll(".wp-block-heading") + ).find(($heading) => $heading.textContent.startsWith("Download")); + + /* Side note: CPG often misspells "Magnet" as "Magent" */ + const $magnet = Array.from($post.querySelectorAll("a")).find( + ($a) => + $a.textContent.startsWith("Magnet") || + $a.textContent.startsWith("Magent") + ); + + const fileSize = $downloadInfo.textContent + .split("Download link => ") + .at(1); + + repacks.push({ + title: $title.textContent, + fileSize: fileSize ?? "N/A", + magnet: $magnet.href, + repacker: "CPG", + page, + uploadDate: new Date(uploadDate), + }); + }); + } catch (err) { + logger.error(err.message, { method: "getNewRepacksFromCPG" }); + } + + const newRepacks = repacks.filter( + (repack) => + repack.uploadDate && + !existingRepacks.some( + (existingRepack) => existingRepack.title === repack.title + ) + ); + + if (!newRepacks.length) return; + + await savePage(newRepacks); + + return getNewRepacksFromCPG(existingRepacks, page + 1); +}; diff --git a/src/main/services/repack-tracker/gog.ts b/src/main/services/repack-tracker/gog.ts new file mode 100644 index 00000000..73daebf7 --- /dev/null +++ b/src/main/services/repack-tracker/gog.ts @@ -0,0 +1,78 @@ +import { JSDOM, VirtualConsole } from "jsdom"; +import { GameRepackInput, requestWebPage, savePage } from "./helpers"; +import { Repack } from "@main/entity"; +import { logger } from "../logger"; + +const virtualConsole = new VirtualConsole(); + +const getGOGGame = async (url: string) => { + const data = await requestWebPage(url); + const { window } = new JSDOM(data, { virtualConsole }); + + const $modifiedTime = window.document.querySelector( + '[property="article:modified_time"]' + ) as HTMLMetaElement; + + const $em = window.document.querySelector( + "p:not(.lightweight-accordion *) em" + ); + const fileSize = $em.textContent.split("Size: ").at(1); + const $downloadButton = window.document.querySelector( + ".download-btn:not(.lightweight-accordion *)" + ) as HTMLAnchorElement; + + const { searchParams } = new URL($downloadButton.href); + const magnet = Buffer.from(searchParams.get("url"), "base64").toString( + "utf-8" + ); + + return { + fileSize: fileSize ?? "N/A", + uploadDate: new Date($modifiedTime.content), + repacker: "GOG", + magnet, + page: 1, + }; +}; + +export const getNewGOGGames = async (existingRepacks: Repack[] = []) => { + try { + const data = await requestWebPage( + "https://freegogpcgames.com/a-z-games-list/" + ); + + const { window } = new JSDOM(data, { virtualConsole }); + + const $uls = Array.from(window.document.querySelectorAll(".az-columns")); + + for (const $ul of $uls) { + const repacks: GameRepackInput[] = []; + const $lis = Array.from($ul.querySelectorAll("li")); + + for (const $li of $lis) { + const $a = $li.querySelector("a"); + const href = $a.href; + + const title = $a.textContent.trim(); + + const gameExists = existingRepacks.some( + (existingRepack) => existingRepack.title === title + ); + + if (!gameExists) { + try { + const game = await getGOGGame(href); + + repacks.push({ ...game, title }); + } catch (err) { + logger.error(err.message, { method: "getGOGGame", url: href }); + } + } + } + + if (repacks.length) await savePage(repacks); + } + } catch (err) { + logger.error(err.message, { method: "getNewGOGGames" }); + } +}; diff --git a/src/main/services/repack-tracker/helpers.ts b/src/main/services/repack-tracker/helpers.ts new file mode 100644 index 00000000..f30bf355 --- /dev/null +++ b/src/main/services/repack-tracker/helpers.ts @@ -0,0 +1,18 @@ +import { repackRepository } from "@main/repository"; + +import type { GameRepack } from "@types"; + +export type GameRepackInput = Omit< + GameRepack, + "id" | "repackerFriendlyName" | "createdAt" | "updatedAt" +>; + +export const savePage = async (repacks: GameRepackInput[]) => + Promise.all( + repacks.map((repack) => repackRepository.insert(repack).catch(() => {})) + ); + +export const requestWebPage = async (url: string) => + fetch(url, { + method: "GET", + }).then((response) => response.text()); diff --git a/src/main/services/repack-tracker/index.ts b/src/main/services/repack-tracker/index.ts new file mode 100644 index 00000000..fb345fae --- /dev/null +++ b/src/main/services/repack-tracker/index.ts @@ -0,0 +1,5 @@ +export * from "./1337x"; +export * from "./xatab"; +export * from "./cpg-repacks"; +export * from "./gog"; +// export * from "./online-fix"; diff --git a/src/main/services/repack-tracker/online-fix.ts b/src/main/services/repack-tracker/online-fix.ts new file mode 100644 index 00000000..c572629c --- /dev/null +++ b/src/main/services/repack-tracker/online-fix.ts @@ -0,0 +1,207 @@ +import { Repack } from "@main/entity"; +import { savePage } from "./helpers"; +import type { GameRepackInput } from "./helpers"; +import { logger } from "../logger"; +import parseTorrent, { + toMagnetURI, + Instance as TorrentInstance, +} from "parse-torrent"; +import { JSDOM } from "jsdom"; +import { gotScraping } from "got-scraping"; +import { CookieJar } from "tough-cookie"; + +import { format, parse, sub } from "date-fns"; +import { ru } from "date-fns/locale"; +import { decode } from "windows-1251"; +import { onlinefixFormatter } from "@main/helpers"; + +export const getNewRepacksFromOnlineFix = async ( + existingRepacks: Repack[] = [], + page = 1, + cookieJar = new CookieJar() +): Promise => { + const hasCredentials = + import.meta.env.MAIN_VITE_ONLINEFIX_USERNAME && + import.meta.env.MAIN_VITE_ONLINEFIX_PASSWORD; + if (!hasCredentials) return; + + const http = gotScraping.extend({ + headerGeneratorOptions: { + browsers: [ + { + name: "chrome", + minVersion: 87, + maxVersion: 89, + }, + ], + devices: ["desktop"], + locales: ["en-US"], + operatingSystems: ["windows", "linux"], + }, + cookieJar: cookieJar, + }); + + if (page === 1) { + await http.get("https://online-fix.me/"); + const preLogin = + ((await http + .get("https://online-fix.me/engine/ajax/authtoken.php", { + headers: { + "X-Requested-With": "XMLHttpRequest", + Referer: "https://online-fix.me/", + }, + }) + .json()) as { + field: string; + value: string; + }) || undefined; + + if (!preLogin.field || !preLogin.value) return; + + const params = new URLSearchParams({ + login_name: import.meta.env.MAIN_VITE_ONLINEFIX_USERNAME, + login_password: import.meta.env.MAIN_VITE_ONLINEFIX_PASSWORD, + login: "submit", + [preLogin.field]: preLogin.value, + }); + + await http + .post("https://online-fix.me/", { + encoding: "binary", + headers: { + Referer: "https://online-fix.me", + Origin: "https://online-fix.me", + "Content-Type": "application/x-www-form-urlencoded", + }, + body: params.toString(), + }) + .text(); + } + + const pageParams = page > 1 ? `${`/page/${page}`}` : ""; + + const home = await http.get(`https://online-fix.me${pageParams}`, { + encoding: "binary", + }); + const document = new JSDOM(home.body).window.document; + + const repacks: GameRepackInput[] = []; + const articles = Array.from(document.querySelectorAll(".news")); + const totalPages = Number( + document.querySelector("nav > a:nth-child(13)").textContent + ); + + try { + await Promise.all( + articles.map(async (article) => { + const gameName = onlinefixFormatter( + decode(article.querySelector("h2.title")?.textContent?.trim()) + ); + + const gameLink = article.querySelector("a")?.getAttribute("href"); + + if (!gameLink) return; + + const gamePage = await http + .get(gameLink, { + encoding: "binary", + }) + .text(); + + const gameDocument = new JSDOM(gamePage).window.document; + + const uploadDateText = gameDocument.querySelector("time").textContent; + + let decodedDateText = decode(uploadDateText); + + // "Вчера" means yesterday. + if (decodedDateText.includes("Вчера")) { + const yesterday = sub(new Date(), { days: 1 }); + const formattedYesterday = format(yesterday, "d LLLL yyyy", { + locale: ru, + }); + decodedDateText = decodedDateText.replace( + "Вчера", // "Change yesterday to the default expected date format" + formattedYesterday + ); + } + + const uploadDate = parse( + decodedDateText, + "d LLLL yyyy, HH:mm", + new Date(), + { + locale: ru, + } + ); + + const torrentButtons = Array.from( + gameDocument.querySelectorAll("a") + ).filter((a) => a.textContent?.includes("Torrent")); + + const torrentPrePage = torrentButtons[0]?.getAttribute("href"); + if (!torrentPrePage) return; + + const torrentPage = await http + .get(torrentPrePage, { + encoding: "binary", + headers: { + Referer: gameLink, + }, + }) + .text(); + + const torrentDocument = new JSDOM(torrentPage).window.document; + + const torrentLink = torrentDocument + .querySelector("a:nth-child(2)") + ?.getAttribute("href"); + + const torrentFile = Buffer.from( + await http + .get(`${torrentPrePage}/${torrentLink}`, { + responseType: "buffer", + }) + .buffer() + ); + + const torrent = parseTorrent(torrentFile) as TorrentInstance; + const magnetLink = toMagnetURI({ + infoHash: torrent.infoHash, + }); + + const torrentSizeInBytes = torrent.length; + const fileSizeFormatted = + torrentSizeInBytes >= 1024 ** 3 + ? `${(torrentSizeInBytes / 1024 ** 3).toFixed(1)}GBs` + : `${(torrentSizeInBytes / 1024 ** 2).toFixed(1)}MBs`; + + repacks.push({ + fileSize: fileSizeFormatted, + magnet: magnetLink, + page: 1, + repacker: "onlinefix", + title: gameName, + uploadDate: uploadDate, + }); + }) + ); + } catch (err) { + logger.error(err.message, { method: "getNewRepacksFromOnlineFix" }); + } + + const newRepacks = repacks.filter( + (repack) => + repack.uploadDate && + !existingRepacks.some( + (existingRepack) => existingRepack.title === repack.title + ) + ); + + if (!newRepacks.length) return; + if (page === totalPages) return; + + await savePage(newRepacks); + + return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar); +}; diff --git a/src/main/services/repack-tracker/xatab.ts b/src/main/services/repack-tracker/xatab.ts new file mode 100644 index 00000000..91a0a4c4 --- /dev/null +++ b/src/main/services/repack-tracker/xatab.ts @@ -0,0 +1,95 @@ +import { JSDOM } from "jsdom"; + +import parseTorrent, { toMagnetURI } from "parse-torrent"; + +import { Repack } from "@main/entity"; +import { logger } from "../logger"; +import { requestWebPage, savePage } from "./helpers"; +import type { GameRepackInput } from "./helpers"; + +const getTorrentBuffer = (url: string) => + fetch(url, { method: "GET" }).then((response) => + response.arrayBuffer().then((buffer) => Buffer.from(buffer)) + ); + +const formatXatabDate = (str: string) => { + const date = new Date(); + + const [day, month, year] = str.split("."); + + date.setDate(Number(day)); + date.setMonth(Number(month) - 1); + date.setFullYear(Number(year)); + date.setHours(0, 0, 0, 0); + + return date; +}; + +const formatXatabDownloadSize = (str: string) => + str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB"); + +const getXatabRepack = async (url: string) => { + const data = await requestWebPage(url); + const { window } = new JSDOM(data); + + const $uploadDate = window.document.querySelector(".entry__date"); + const $size = window.document.querySelector(".entry__info-size"); + + const $downloadButton = window.document.querySelector( + ".download-torrent" + ) as HTMLAnchorElement; + + if (!$downloadButton) throw new Error("Download button not found"); + + const torrentBuffer = await getTorrentBuffer($downloadButton.href); + + return { + fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(), + magnet: toMagnetURI({ + infoHash: parseTorrent(torrentBuffer).infoHash, + }), + uploadDate: formatXatabDate($uploadDate.textContent), + }; +}; + +export const getNewRepacksFromXatab = async ( + existingRepacks: Repack[] = [], + page = 1 +): Promise => { + const data = await requestWebPage(`https://byxatab.com/page/${page}`); + + const { window } = new JSDOM(data); + + const repacks: GameRepackInput[] = []; + + for (const $a of Array.from( + window.document.querySelectorAll(".entry__title a") + )) { + try { + const repack = await getXatabRepack(($a as HTMLAnchorElement).href); + + repacks.push({ + title: $a.textContent, + repacker: "Xatab", + ...repack, + page, + }); + } catch (err) { + logger.error(err.message, { method: "getNewRepacksFromXatab" }); + } + } + + const newRepacks = repacks.filter( + (repack) => + repack.uploadDate && + !existingRepacks.some( + (existingRepack) => existingRepack.title === repack.title + ) + ); + + if (!newRepacks.length) return; + + await savePage(newRepacks); + + return getNewRepacksFromXatab(existingRepacks, page + 1); +}; diff --git a/src/main/services/steam-250.ts b/src/main/services/steam-250.ts new file mode 100644 index 00000000..32ccdc56 --- /dev/null +++ b/src/main/services/steam-250.ts @@ -0,0 +1,34 @@ +import axios from "axios"; +import { JSDOM } from "jsdom"; +import { shuffle } from "lodash-es"; + +export const requestSteam250 = async (path: string) => { + return axios.get(`https://steam250.com${path}`).then((response) => { + const { window } = new JSDOM(response.data); + const { document } = window; + + return Array.from(document.querySelectorAll(".appline .title a")).map( + ($title: HTMLAnchorElement) => { + const steamGameUrl = $title.href; + if (!steamGameUrl) return null; + + return { + title: $title.textContent, + objectID: steamGameUrl.split("/").pop(), + }; + } + ); + }); +}; + +const steam250Paths = [ + "/hidden_gems", + `/${new Date().getFullYear()}`, + "/top250", + "/most_played", +]; + +export const getRandomSteam250List = async () => { + const [path] = shuffle(steam250Paths); + return requestSteam250(path); +}; diff --git a/src/main/services/steam-grid.ts b/src/main/services/steam-grid.ts new file mode 100644 index 00000000..9e2ce9d8 --- /dev/null +++ b/src/main/services/steam-grid.ts @@ -0,0 +1,71 @@ +import { getSteamAppAsset } from "@main/helpers"; + +export interface SteamGridResponse { + success: boolean; + data: { + id: number; + }; +} + +export interface SteamGridGameResponse { + data: { + platforms: { + steam: { + metadata: { + clienticon: string; + }; + }; + }; + }; +} + +export const getSteamGridData = async ( + objectID: string, + path: string, + shop: string, + params: Record = {} +): Promise => { + const searchParams = new URLSearchParams(params); + + const response = await fetch( + `https://www.steamgriddb.com/api/v2/${path}/${shop}/${objectID}?${searchParams.toString()}`, + { + method: "GET", + headers: { + Authorization: `Bearer ${import.meta.env.MAIN_VITE_STEAMGRIDDB_API_KEY}`, + }, + } + ); + + return response.json(); +}; + +export const getSteamGridGameById = async ( + id: number +): Promise => { + const response = await fetch( + `https://www.steamgriddb.com/api/public/game/${id}`, + { + method: "GET", + headers: { + Referer: "https://www.steamgriddb.com/", + }, + } + ); + + return response.json(); +}; + +export const getSteamGameIconUrl = async (objectID: string) => { + const { + data: { id: steamGridGameId }, + } = await getSteamGridData(objectID, "games", "steam"); + + const steamGridGame = await getSteamGridGameById(steamGridGameId); + + return getSteamAppAsset( + "icon", + objectID, + steamGridGame.data.platforms.steam.metadata.clienticon + ); +}; diff --git a/src/main/services/steam.ts b/src/main/services/steam.ts new file mode 100644 index 00000000..0f2d4987 --- /dev/null +++ b/src/main/services/steam.ts @@ -0,0 +1,35 @@ +import axios from "axios"; + +import type { SteamAppDetails } from "@types"; + +import { logger } from "./logger"; + +export interface SteamAppDetailsResponse { + [key: string]: { + success: boolean; + data: SteamAppDetails; + }; +} + +export const getSteamAppDetails = async ( + objectID: string, + language: string +) => { + const searchParams = new URLSearchParams({ + appids: objectID, + l: language, + }); + + return axios + .get( + `http://store.steampowered.com/api/appdetails?${searchParams.toString()}` + ) + .then((response) => { + if (response.data[objectID].success) return response.data[objectID].data; + return null; + }) + .catch((err) => { + logger.error(err, { method: "getSteamAppDetails" }); + throw new Error(err); + }); +}; diff --git a/src/main/services/torrent-client.ts b/src/main/services/torrent-client.ts new file mode 100644 index 00000000..fa1cd59d --- /dev/null +++ b/src/main/services/torrent-client.ts @@ -0,0 +1,170 @@ +import path from "node:path"; +import cp from "node:child_process"; +import fs from "node:fs"; +import * as Sentry from "@sentry/electron/main"; +import { Notification, app, dialog } from "electron"; +import type { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; + +import { Game } from "@main/entity"; +import { gameRepository, userPreferencesRepository } from "@main/repository"; +import { t } from "i18next"; +import { WindowManager } from "./window-manager"; + +const binaryNameByPlatform: Partial> = { + darwin: "hydra-download-manager", + linux: "hydra-download-manager", + win32: "hydra-download-manager.exe", +}; + +enum TorrentState { + CheckingFiles = 1, + DownloadingMetadata = 2, + Downloading = 3, + Finished = 4, + Seeding = 5, +} + +export interface TorrentUpdate { + gameId: number; + progress: number; + downloadSpeed: number; + timeRemaining: number; + numPeers: number; + numSeeds: number; + status: TorrentState; + folderName: string; + fileSize: number; + bytesDownloaded: number; +} + +export const BITTORRENT_PORT = "5881"; + +export class TorrentClient { + public static startTorrentClient( + writePipePath: string, + readPipePath: string + ) { + const commonArgs = [BITTORRENT_PORT, writePipePath, readPipePath]; + + if (app.isPackaged) { + const binaryName = binaryNameByPlatform[process.platform]; + const binaryPath = path.join( + process.resourcesPath, + "dist", + "hydra-download-manager", + binaryName + ); + + if (!fs.existsSync(binaryPath)) { + dialog.showErrorBox( + "Fatal", + "Hydra download manager binary not found. Please check if it has been removed by Windows Defender." + ); + + app.quit(); + } + + cp.spawn(binaryPath, commonArgs, { + stdio: "inherit", + windowsHide: true, + }); + return; + } + + const scriptPath = path.join( + __dirname, + "..", + "..", + "torrent-client", + "main.py" + ); + + cp.spawn("python3", [scriptPath, ...commonArgs], { + stdio: "inherit", + }); + } + + private static getTorrentStateName(state: TorrentState) { + if (state === TorrentState.CheckingFiles) return "checking_files"; + if (state === TorrentState.Downloading) return "downloading"; + if (state === TorrentState.DownloadingMetadata) + return "downloading_metadata"; + if (state === TorrentState.Finished) return "finished"; + if (state === TorrentState.Seeding) return "seeding"; + return ""; + } + + private static getGameProgress(game: Game) { + if (game.status === "checking_files") return game.fileVerificationProgress; + return game.progress; + } + + public static async onSocketData(data: Buffer) { + const message = Buffer.from(data).toString("utf-8"); + + try { + const payload = JSON.parse(message) as TorrentUpdate; + + const updatePayload: QueryDeepPartialEntity = { + bytesDownloaded: payload.bytesDownloaded, + status: this.getTorrentStateName(payload.status), + }; + + if (payload.status === TorrentState.CheckingFiles) { + updatePayload.fileVerificationProgress = payload.progress; + } else { + if (payload.folderName) { + updatePayload.folderName = payload.folderName; + updatePayload.fileSize = payload.fileSize; + } + } + + if ( + [TorrentState.Downloading, TorrentState.Seeding].includes( + payload.status + ) + ) { + updatePayload.progress = payload.progress; + } + + await gameRepository.update({ id: payload.gameId }, updatePayload); + + const game = await gameRepository.findOne({ + where: { id: payload.gameId }, + relations: { repack: true }, + }); + + if (game.progress === 1) { + const userPreferences = await userPreferencesRepository.findOne({ + where: { id: 1 }, + }); + + if (userPreferences?.downloadNotificationsEnabled) { + new Notification({ + title: t("download_complete", { + ns: "notifications", + lng: userPreferences.language, + }), + body: t("game_ready_to_install", { + ns: "notifications", + lng: userPreferences.language, + title: game.title, + }), + }).show(); + } + } + + if (WindowManager.mainWindow) { + const progress = this.getGameProgress(game); + WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress); + + WindowManager.mainWindow.webContents.send( + "on-download-progress", + JSON.parse(JSON.stringify({ ...payload, game })) + ); + } + } catch (err) { + Sentry.captureException(err); + } + } +} diff --git a/src/main/services/update-resolver.ts b/src/main/services/update-resolver.ts new file mode 100644 index 00000000..8701d17d --- /dev/null +++ b/src/main/services/update-resolver.ts @@ -0,0 +1,159 @@ +import path from "node:path"; +import { app } from "electron"; + +import { chunk } from "lodash-es"; + +import { createDataSource, dataSource } from "@main/data-source"; +import { Repack, RepackerFriendlyName, SteamGame } from "@main/entity"; +import { + migrationScriptRepository, + repackRepository, + repackerFriendlyNameRepository, + steamGameRepository, +} from "@main/repository"; +import { MigrationScript } from "@main/entity/migration-script.entity"; +import { Like } from "typeorm"; + +const migrationScripts = { + /* + 0.0.6 -> 0.0.7 + Xatab repacks were previously created with an incorrect upload date. + This migration script will update the upload date of all Xatab repacks. + */ + "0.0.7": async (updateRepacks: Repack[]) => { + const VERSION = "0.0.7"; + + const migrationScript = await migrationScriptRepository.findOne({ + where: { + version: VERSION, + }, + }); + + if (!migrationScript) { + const xatabRepacks = updateRepacks.filter( + (repack) => repack.repacker === "Xatab" + ); + + await dataSource.transaction(async (transactionalEntityManager) => { + await Promise.all( + xatabRepacks.map((repack) => + transactionalEntityManager.getRepository(Repack).update( + { + title: repack.title, + repacker: repack.repacker, + }, + { + uploadDate: repack.uploadDate, + } + ) + ) + ); + + await transactionalEntityManager.getRepository(MigrationScript).insert({ + version: VERSION, + }); + }); + } + }, + /* + 1.0.1 -> 1.1.0 + A few torrents scraped from 1337x were previously created with an incorrect upload date. + */ + "1.1.0": async () => { + const VERSION = "1.1.0"; + + const migrationScript = await migrationScriptRepository.findOne({ + where: { + version: VERSION, + }, + }); + + if (!migrationScript) { + await dataSource.transaction(async (transactionalEntityManager) => { + const repacks = await transactionalEntityManager + .getRepository(Repack) + .find({ + where: { + uploadDate: Like("1%"), + }, + }); + + await Promise.all( + repacks.map(async (repack) => { + return transactionalEntityManager + .getRepository(Repack) + .update( + { id: repack.id }, + { uploadDate: new Date(repack.uploadDate) } + ); + }) + ); + + await transactionalEntityManager.getRepository(MigrationScript).insert({ + version: VERSION, + }); + }); + } + }, +}; + +export const runMigrationScripts = async (updateRepacks: Repack[]) => { + return Promise.all( + Object.values(migrationScripts).map((migrationScript) => { + return migrationScript(updateRepacks); + }) + ); +}; + +export const resolveDatabaseUpdates = async () => { + const updateDataSource = createDataSource({ + database: app.isPackaged + ? path.join(process.resourcesPath, "hydra.db") + : path.join(__dirname, "..", "..", "resources", "hydra.db"), + }); + + return updateDataSource.initialize().then(async () => { + const updateRepackRepository = updateDataSource.getRepository(Repack); + const updateRepackerFriendlyNameRepository = + updateDataSource.getRepository(RepackerFriendlyName); + const updateSteamGameRepository = updateDataSource.getRepository(SteamGame); + + const [updateRepacks, updateSteamGames, updateRepackerFriendlyNames] = + await Promise.all([ + updateRepackRepository.find(), + updateSteamGameRepository.find(), + updateRepackerFriendlyNameRepository.find(), + ]); + + await runMigrationScripts(updateRepacks); + + await repackerFriendlyNameRepository + .createQueryBuilder() + .insert() + .values(updateRepackerFriendlyNames) + .orIgnore() + .execute(); + + const updateRepacksChunks = chunk(updateRepacks, 800); + + for (const chunk of updateRepacksChunks) { + await repackRepository + .createQueryBuilder() + .insert() + .values(chunk) + .orIgnore() + .execute(); + } + + const steamGamesChunks = chunk(updateSteamGames, 800); + + for (const chunk of steamGamesChunks) { + await steamGameRepository + .createQueryBuilder() + .insert() + .values(chunk) + .orIgnore() + .execute(); + } + }); +}; diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts new file mode 100644 index 00000000..d8b174a2 --- /dev/null +++ b/src/main/services/window-manager.ts @@ -0,0 +1,109 @@ +import { BrowserWindow, Menu, Tray, app } from "electron"; +import { electronApp, optimizer, is } from "@electron-toolkit/utils"; +import { t } from "i18next"; +import path from "node:path"; + +export class WindowManager { + public static mainWindow: Electron.BrowserWindow | null = null; + + public static createMainWindow() { + // Create the browser window. + this.mainWindow = new BrowserWindow({ + width: 1200, + height: 720, + minWidth: 1024, + minHeight: 540, + titleBarStyle: "hidden", + // icon: path.join(__dirname, "..", "..", "images", "icon.png"), + trafficLightPosition: { x: 16, y: 16 }, + titleBarOverlay: { + symbolColor: "#DADBE1", + color: "#151515", + height: 34, + }, + webPreferences: { + preload: path.join(__dirname, "../preload/index.mjs"), + sandbox: false, + }, + }); + + this.mainWindow.removeMenu(); + + // HMR for renderer base on electron-vite cli. + // Load the remote URL for development or the local html file for production. + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + this.mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]); + } else { + this.mainWindow.loadFile(path.join(__dirname, "../renderer/index.html")); + } + + this.mainWindow.webContents.on("did-finish-load", () => { + if (!app.isPackaged) { + // Open the DevTools. + this.mainWindow.webContents.openDevTools(); + } + }); + + this.mainWindow.on("close", () => { + WindowManager.mainWindow.setProgressBar(-1); + }); + } + + public static redirect(path: string) { + if (!this.mainWindow) this.createMainWindow(); + this.mainWindow.loadURL(`${MAIN_WINDOW_WEBPACK_ENTRY}#${path}`); + + if (this.mainWindow.isMinimized()) this.mainWindow.restore(); + this.mainWindow.focus(); + } + + public static createSystemTray(language: string) { + const tray = new Tray( + app.isPackaged + ? path.join(process.resourcesPath, "icon_tray.png") + : path.join(__dirname, "..", "..", "resources", "icon_tray.png") + ); + + const contextMenu = Menu.buildFromTemplate([ + { + label: t("open", { + ns: "system_tray", + lng: language, + }), + type: "normal", + click: () => { + if (this.mainWindow) { + this.mainWindow.show(); + } else { + this.createMainWindow(); + } + }, + }, + { + label: t("quit", { + ns: "system_tray", + lng: language, + }), + type: "normal", + click: () => app.quit(), + }, + ]); + + tray.setToolTip("Hydra"); + tray.setContextMenu(contextMenu); + + if (process.platform === "win32") { + tray.addListener("click", () => { + if (this.mainWindow) { + if (WindowManager.mainWindow.isMinimized()) + WindowManager.mainWindow.restore(); + + WindowManager.mainWindow.focus(); + return; + } + + this.createMainWindow(); + }); + } + } +} diff --git a/src/main/state-manager.ts b/src/main/state-manager.ts new file mode 100644 index 00000000..6dec3985 --- /dev/null +++ b/src/main/state-manager.ts @@ -0,0 +1,33 @@ +import type { Repack, RepackerFriendlyName, SteamGame } from "@main/entity"; + +interface State { + repacks: Repack[]; + repackersFriendlyNames: RepackerFriendlyName[]; + steamGames: SteamGame[]; + eventResults: Map<[string, any[]], any>; +} + +const initialState: State = { + repacks: [], + repackersFriendlyNames: [], + steamGames: [], + eventResults: new Map(), +}; + +export class StateManager { + private state = initialState; + + public setValue(key: T, value: State[T]) { + this.state = { ...this.state, [key]: value }; + } + + public getValue(key: T) { + return this.state[key]; + } + + public clearValue(key: T) { + this.state = { ...this.state, [key]: initialState[key] }; + } +} + +export const stateManager = new StateManager(); diff --git a/src/main/vite-env.d.ts b/src/main/vite-env.d.ts new file mode 100644 index 00000000..2c277d37 --- /dev/null +++ b/src/main/vite-env.d.ts @@ -0,0 +1,12 @@ +/// + +interface ImportMetaEnv { + readonly MAIN_VITE_STEAMGRIDDB_API_KEY: string; + readonly MAIN_VITE_ONLINEFIX_USERNAME: string; + readonly MAIN_VITE_ONLINEFIX_PASSWORD: string; + readonly MAIN_VITE_SENTRY_DSN: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts new file mode 100644 index 00000000..97dcca0b --- /dev/null +++ b/src/preload/index.d.ts @@ -0,0 +1,104 @@ +// See the Electron documentation for details on how to use preload scripts: +// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts +import { contextBridge, ipcRenderer } from "electron"; + +import type { + CatalogueCategory, + GameShop, + TorrentProgress, + UserPreferences, +} from "@types"; + +contextBridge.exposeInMainWorld("electron", { + /* Torrenting */ + startGameDownload: ( + repackId: number, + objectID: string, + title: string, + shop: GameShop + ) => ipcRenderer.invoke("startGameDownload", repackId, objectID, title, shop), + cancelGameDownload: (gameId: number) => + ipcRenderer.invoke("cancelGameDownload", gameId), + pauseGameDownload: (gameId: number) => + ipcRenderer.invoke("pauseGameDownload", gameId), + resumeGameDownload: (gameId: number) => + ipcRenderer.invoke("resumeGameDownload", gameId), + onDownloadProgress: (cb: (value: TorrentProgress) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + value: TorrentProgress + ) => cb(value); + ipcRenderer.on("on-download-progress", listener); + return () => ipcRenderer.removeListener("on-download-progress", listener); + }, + + /* Catalogue */ + searchGames: (query: string) => ipcRenderer.invoke("searchGames", query), + getCatalogue: (category: CatalogueCategory) => + ipcRenderer.invoke("getCatalogue", category), + getGameShopDetails: (objectID: string, shop: GameShop, language: string) => + ipcRenderer.invoke("getGameShopDetails", objectID, shop, language), + getRandomGame: () => ipcRenderer.invoke("getRandomGame"), + getHowLongToBeat: (objectID: string, shop: GameShop, title: string) => + ipcRenderer.invoke("getHowLongToBeat", objectID, shop, title), + getGames: (take?: number, prevCursor?: number) => + ipcRenderer.invoke("getGames", take, prevCursor), + + /* User preferences */ + getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"), + updateUserPreferences: (preferences: UserPreferences) => + ipcRenderer.invoke("updateUserPreferences", preferences), + + /* Library */ + addGameToLibrary: ( + objectID: string, + title: string, + shop: GameShop, + executablePath: string + ) => + ipcRenderer.invoke( + "addGameToLibrary", + objectID, + title, + shop, + executablePath + ), + getLibrary: () => ipcRenderer.invoke("getLibrary"), + getRepackersFriendlyNames: () => + ipcRenderer.invoke("getRepackersFriendlyNames"), + openGameInstaller: (gameId: number) => + ipcRenderer.invoke("openGameInstaller", gameId), + openGame: (gameId: number, executablePath: string) => + ipcRenderer.invoke("openGame", gameId, executablePath), + closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), + removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), + deleteGameFolder: (gameId: number) => + ipcRenderer.invoke("deleteGameFolder", gameId), + getGameByObjectID: (objectID: string) => + ipcRenderer.invoke("getGameByObjectID", objectID), + onPlaytime: (cb: (gameId: number) => void) => { + const listener = (_event: Electron.IpcRendererEvent, gameId: number) => + cb(gameId); + ipcRenderer.on("on-playtime", listener); + return () => ipcRenderer.removeListener("on-playtime", listener); + }, + onGameClose: (cb: (gameId: number) => void) => { + const listener = (_event: Electron.IpcRendererEvent, gameId: number) => + cb(gameId); + ipcRenderer.on("on-game-close", listener); + return () => ipcRenderer.removeListener("on-game-close", listener); + }, + + /* Hardware */ + getDiskFreeSpace: () => ipcRenderer.invoke("getDiskFreeSpace"), + + /* Misc */ + getOrCacheImage: (url: string) => ipcRenderer.invoke("getOrCacheImage", url), + ping: () => ipcRenderer.invoke("ping"), + getVersion: () => ipcRenderer.invoke("getVersion"), + getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"), + openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), + showOpenDialog: (options: Electron.OpenDialogOptions) => + ipcRenderer.invoke("showOpenDialog", options), + platform: process.platform, +}); diff --git a/src/preload/index.ts b/src/preload/index.ts new file mode 100644 index 00000000..97dcca0b --- /dev/null +++ b/src/preload/index.ts @@ -0,0 +1,104 @@ +// See the Electron documentation for details on how to use preload scripts: +// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts +import { contextBridge, ipcRenderer } from "electron"; + +import type { + CatalogueCategory, + GameShop, + TorrentProgress, + UserPreferences, +} from "@types"; + +contextBridge.exposeInMainWorld("electron", { + /* Torrenting */ + startGameDownload: ( + repackId: number, + objectID: string, + title: string, + shop: GameShop + ) => ipcRenderer.invoke("startGameDownload", repackId, objectID, title, shop), + cancelGameDownload: (gameId: number) => + ipcRenderer.invoke("cancelGameDownload", gameId), + pauseGameDownload: (gameId: number) => + ipcRenderer.invoke("pauseGameDownload", gameId), + resumeGameDownload: (gameId: number) => + ipcRenderer.invoke("resumeGameDownload", gameId), + onDownloadProgress: (cb: (value: TorrentProgress) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + value: TorrentProgress + ) => cb(value); + ipcRenderer.on("on-download-progress", listener); + return () => ipcRenderer.removeListener("on-download-progress", listener); + }, + + /* Catalogue */ + searchGames: (query: string) => ipcRenderer.invoke("searchGames", query), + getCatalogue: (category: CatalogueCategory) => + ipcRenderer.invoke("getCatalogue", category), + getGameShopDetails: (objectID: string, shop: GameShop, language: string) => + ipcRenderer.invoke("getGameShopDetails", objectID, shop, language), + getRandomGame: () => ipcRenderer.invoke("getRandomGame"), + getHowLongToBeat: (objectID: string, shop: GameShop, title: string) => + ipcRenderer.invoke("getHowLongToBeat", objectID, shop, title), + getGames: (take?: number, prevCursor?: number) => + ipcRenderer.invoke("getGames", take, prevCursor), + + /* User preferences */ + getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"), + updateUserPreferences: (preferences: UserPreferences) => + ipcRenderer.invoke("updateUserPreferences", preferences), + + /* Library */ + addGameToLibrary: ( + objectID: string, + title: string, + shop: GameShop, + executablePath: string + ) => + ipcRenderer.invoke( + "addGameToLibrary", + objectID, + title, + shop, + executablePath + ), + getLibrary: () => ipcRenderer.invoke("getLibrary"), + getRepackersFriendlyNames: () => + ipcRenderer.invoke("getRepackersFriendlyNames"), + openGameInstaller: (gameId: number) => + ipcRenderer.invoke("openGameInstaller", gameId), + openGame: (gameId: number, executablePath: string) => + ipcRenderer.invoke("openGame", gameId, executablePath), + closeGame: (gameId: number) => ipcRenderer.invoke("closeGame", gameId), + removeGame: (gameId: number) => ipcRenderer.invoke("removeGame", gameId), + deleteGameFolder: (gameId: number) => + ipcRenderer.invoke("deleteGameFolder", gameId), + getGameByObjectID: (objectID: string) => + ipcRenderer.invoke("getGameByObjectID", objectID), + onPlaytime: (cb: (gameId: number) => void) => { + const listener = (_event: Electron.IpcRendererEvent, gameId: number) => + cb(gameId); + ipcRenderer.on("on-playtime", listener); + return () => ipcRenderer.removeListener("on-playtime", listener); + }, + onGameClose: (cb: (gameId: number) => void) => { + const listener = (_event: Electron.IpcRendererEvent, gameId: number) => + cb(gameId); + ipcRenderer.on("on-game-close", listener); + return () => ipcRenderer.removeListener("on-game-close", listener); + }, + + /* Hardware */ + getDiskFreeSpace: () => ipcRenderer.invoke("getDiskFreeSpace"), + + /* Misc */ + getOrCacheImage: (url: string) => ipcRenderer.invoke("getOrCacheImage", url), + ping: () => ipcRenderer.invoke("ping"), + getVersion: () => ipcRenderer.invoke("getVersion"), + getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"), + openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), + showOpenDialog: (options: Electron.OpenDialogOptions) => + ipcRenderer.invoke("showOpenDialog", options), + platform: process.platform, +}); diff --git a/src/renderer/index.html b/src/renderer/index.html new file mode 100644 index 00000000..027e5085 --- /dev/null +++ b/src/renderer/index.html @@ -0,0 +1,17 @@ + + + + + Electron + + + + + +
+ + + diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts new file mode 100644 index 00000000..fc104adc --- /dev/null +++ b/src/renderer/src/app.css.ts @@ -0,0 +1,107 @@ +import { ComplexStyleRule, globalStyle, style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "./theme.css"; + +globalStyle("*", { + boxSizing: "border-box", +}); + +globalStyle("::-webkit-scrollbar", { + width: "9px", +}); + +globalStyle("::-webkit-scrollbar-track", { + backgroundColor: "rgba(255, 255, 255, 0.03)", +}); + +globalStyle("::-webkit-scrollbar-thumb", { + backgroundColor: "rgba(255, 255, 255, 0.08)", + borderRadius: "24px", +}); + +globalStyle("html, body, #root, main", { + height: "100%", +}); + +globalStyle("body", { + overflow: "hidden", + userSelect: "none", + fontFamily: "'Fira Mono', monospace", + background: vars.color.background, + color: vars.color.bodyText, + margin: "0", +}); + +globalStyle("button", { + padding: "0", + backgroundColor: "transparent", + border: "none", + fontFamily: "inherit", + fontSize: vars.size.bodyFontSize, +}); + +globalStyle("h1, h2, h3, h4, h5, h6, p", { + margin: 0, +}); + +globalStyle("#root, main", { + display: "flex", +}); + +globalStyle("#root", { + flexDirection: "column", +}); + +globalStyle("main", { + overflow: "hidden", +}); + +globalStyle( + "input::-webkit-outer-spin-button, input::-webkit-inner-spin-button", + { + WebkitAppearance: "none", + margin: "0", + } +); + +globalStyle("label", { + fontSize: vars.size.bodyFontSize, +}); + +globalStyle("input[type=number]", { + MozAppearance: "textfield", +}); + +globalStyle("img", { + WebkitUserDrag: "none", +} as Record); + +export const container = style({ + width: "100%", + height: "100%", + overflow: "hidden", + display: "flex", + flexDirection: "column", +}); + +export const content = style({ + overflowY: "auto", + alignItems: "center", + display: "flex", + flexDirection: "column", + position: "relative", + height: "100%", + background: `linear-gradient(0deg, ${vars.color.darkBackground} 50%, ${vars.color.background} 100%)`, +}); + +export const titleBar = style({ + display: "flex", + width: "100%", + height: "35px", + minHeight: "35px", + backgroundColor: vars.color.darkBackground, + alignItems: "center", + padding: `0 ${SPACING_UNIT * 2}px`, + WebkitAppRegion: "drag", + zIndex: "2", + borderBottom: `1px solid ${vars.color.borderColor}`, +} as ComplexStyleRule); diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx new file mode 100644 index 00000000..4e633bcf --- /dev/null +++ b/src/renderer/src/app.tsx @@ -0,0 +1,122 @@ +import { useCallback, useEffect, useRef } from "react"; + +import { Sidebar, BottomPanel, Header } from "@renderer/components"; + +import { + useAppDispatch, + useAppSelector, + useDownload, + useLibrary, +} from "@renderer/hooks"; + +import * as styles from "./app.css"; +import { themeClass } from "./theme.css"; + +import { Outlet, useLocation, useNavigate } from "react-router-dom"; +import { + setSearch, + clearSearch, + setUserPreferences, + setRepackersFriendlyNames, +} from "@renderer/features"; + +document.body.classList.add(themeClass); + +export function App() { + const contentRef = useRef(null); + const { updateLibrary } = useLibrary(); + + const { clearDownload, addPacket } = useDownload(); + + const dispatch = useAppDispatch(); + + const navigate = useNavigate(); + const location = useLocation(); + + const search = useAppSelector((state) => state.search.value); + + useEffect(() => { + Promise.all([ + window.electron.getUserPreferences(), + window.electron.getRepackersFriendlyNames(), + updateLibrary(), + ]).then(([preferences, repackersFriendlyNames]) => { + dispatch(setUserPreferences(preferences)); + dispatch(setRepackersFriendlyNames(repackersFriendlyNames)); + }); + }, [navigate, location.pathname, dispatch, updateLibrary]); + + useEffect(() => { + const unsubscribe = window.electron.onDownloadProgress( + (downloadProgress) => { + if (downloadProgress.game.progress === 1) { + clearDownload(); + updateLibrary(); + return; + } + + addPacket(downloadProgress); + } + ); + + return () => { + unsubscribe(); + }; + }, [clearDownload, addPacket, updateLibrary]); + + const handleSearch = useCallback( + (query: string) => { + dispatch(setSearch(query)); + + if (query === "") { + navigate(-1); + return; + } + + const searchParams = new URLSearchParams({ + query, + }); + + navigate(`/search?${searchParams.toString()}`, { + replace: location.pathname.startsWith("/search"), + }); + }, + [dispatch, location.pathname, navigate] + ); + + const handleClear = useCallback(() => { + dispatch(clearSearch()); + navigate(-1); + }, [dispatch, navigate]); + + useEffect(() => { + if (contentRef.current) contentRef.current.scrollTop = 0; + }, [location.pathname, location.search]); + + return ( + <> + {window.electron.platform === "win32" && ( +
+

Hydra

+
+ )} + +
+ + +
+
+ +
+ +
+
+
+ + + ); +} diff --git a/src/renderer/src/assets/discord-icon.svg b/src/renderer/src/assets/discord-icon.svg new file mode 100644 index 00000000..2fba46cd --- /dev/null +++ b/src/renderer/src/assets/discord-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/assets/epic-games-logo.svg b/src/renderer/src/assets/epic-games-logo.svg new file mode 100644 index 00000000..a6c53dbc --- /dev/null +++ b/src/renderer/src/assets/epic-games-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/assets/lottie/downloading.json b/src/renderer/src/assets/lottie/downloading.json new file mode 100644 index 00000000..1ef705de --- /dev/null +++ b/src/renderer/src/assets/lottie/downloading.json @@ -0,0 +1,843 @@ +{ + "v": "4.8.0", + "meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" }, + "fr": 60, + "ip": 0, + "op": 120, + "w": 714, + "h": 678, + "nm": "Pre-comp 1", + "ddd": 0, + "assets": [ + { + "id": "comp_0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "centro", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.214, "y": 1 }, + "o": { "x": 0.462, "y": 0 }, + "t": 0, + "s": [450, 907, 0], + "to": [0, 0, 0], + "ti": [0, 0, 0] + }, + { "t": 30, "s": [450, 1513, 0] } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [-348, -169, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-348, -420], + [-348, -30] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-348, -164], "ix": 2 }, + "a": { "a": 0, "k": [-348, -156], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "esquerdo", + "parent": 1, + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.298], "y": [1] }, + "o": { "x": [0.448], "y": [0] }, + "t": 6, + "s": [43.5] + }, + { "t": 36, "s": [-1] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 }, + "a": { "a": 0, "k": [-2, 84, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-178, -102], + [-2, 84] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tm", + "s": { "a": 0, "k": 8, "ix": 1 }, + "e": { "a": 0, "k": 100, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "m": 1, + "ix": 2, + "nm": "Trim Paths 1", + "mn": "ADBE Vector Filter - Trim", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "direito", + "parent": 1, + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.265], "y": [1] }, + "o": { "x": [0.53], "y": [0] }, + "t": 6, + "s": [-43.5] + }, + { "t": 36, "s": [1] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 }, + "a": { "a": 0, "k": [-2, 84, 0], "ix": 1 }, + "s": { "a": 0, "k": [-100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-178, -102], + [-2, 84] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "tm", + "s": { "a": 0, "k": 8, "ix": 1 }, + "e": { "a": 0, "k": 100, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "m": 1, + "ix": 2, + "nm": "Trim Paths 1", + "mn": "ADBE Vector Filter - Trim", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 4, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ] + }, + { + "id": "comp_1", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "centro", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.569, "y": 1 }, + "o": { "x": 0.809, "y": 0 }, + "t": 0, + "s": [450, 391, 0], + "to": [0, 0, 0], + "ti": [0, 0, 0] + }, + { "t": 30, "s": [450, 997, 0] } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [-348, -169, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-348, -420], + [-348, -30] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-348, -164], "ix": 2 }, + "a": { "a": 0, "k": [-348, -156], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "esquerdo", + "parent": 1, + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.552], "y": [1] }, + "o": { "x": [0.702], "y": [0] }, + "t": 0, + "s": [-1] + }, + { "t": 30, "s": [43.5] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 }, + "a": { "a": 0, "k": [-2, 84, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-178, -102], + [-2, 84] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tm", + "s": { "a": 0, "k": 8, "ix": 1 }, + "e": { "a": 0, "k": 100, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "m": 1, + "ix": 2, + "nm": "Trim Paths 1", + "mn": "ADBE Vector Filter - Trim", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "direito", + "parent": 1, + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.47], "y": [1] }, + "o": { "x": [0.735], "y": [0] }, + "t": 0, + "s": [1] + }, + { "t": 30, "s": [-43.5] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [-348.39, -36.55, 0], "ix": 2 }, + "a": { "a": 0, "k": [-2, 84, 0], "ix": 1 }, + "s": { "a": 0, "k": [-100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0] + ], + "v": [ + [-178, -102], + [-2, 84] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "tm", + "s": { "a": 0, "k": 8, "ix": 1 }, + "e": { "a": 0, "k": 100, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "m": 1, + "ix": 2, + "nm": "Trim Paths 1", + "mn": "ADBE Vector Filter - Trim", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [0.854901960784, 0.858823529412, 0.882352941176, 1], + "ix": 3 + }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 77, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 4, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ] + } + ], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 0, + "nm": "seta 2", + "refId": "comp_0", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [357, -247, 0], "ix": 2 }, + "a": { "a": 0, "k": [450, 960, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "w": 900, + "h": 1920, + "ip": 30, + "op": 120, + "st": 30, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 0, + "nm": "seta", + "refId": "comp_1", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [357, 258, 0], "ix": 2 }, + "a": { "a": 0, "k": [450, 345, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "w": 900, + "h": 690, + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "base Outlines", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [357, 548.713, 0], "ix": 2 }, + "a": { "a": 0, "k": [357.81, 129.934, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 50.043], + [0, 0], + [-21.158, 0], + [0, -21.447], + [0, 0], + [-7.049, 0], + [0, 0], + [0, 7.149], + [0, 0], + [-21.158, 0], + [0, -21.447], + [0, 0], + [49.368, 0] + ], + "o": [ + [-49.369, 0], + [0, 0], + [0, -21.447], + [21.158, 0], + [0, 0], + [0, 7.145], + [0, 0], + [7.053, 0], + [0, 0], + [0, -21.447], + [21.158, 0], + [0, 0], + [0, 50.043], + [0, 0] + ], + "v": [ + [-268.169, 129.445], + [-357.559, 38.834], + [-357.559, -90.61], + [-319.249, -129.445], + [-280.939, -90.61], + [-280.939, 38.834], + [-268.169, 51.778], + [268.169, 51.778], + [280.939, 38.834], + [280.939, -90.61], + [319.249, -129.445], + [357.559, -90.61], + [357.559, 38.834], + [268.169, 129.445] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [0.865977448108, 0.86824388691, 0.890449075138, 1], + "ix": 4 + }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [357.809, 129.695], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/src/renderer/src/assets/lottie/settings.json b/src/renderer/src/assets/lottie/settings.json new file mode 100644 index 00000000..92fa645e --- /dev/null +++ b/src/renderer/src/assets/lottie/settings.json @@ -0,0 +1,1054 @@ +{ + "v": "4.8.0", + "meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" }, + "fr": 60, + "ip": 0, + "op": 120, + "w": 900, + "h": 900, + "nm": "Pre-comp 1", + "ddd": 0, + "assets": [ + { + "id": "comp_0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "estrela Outlines 2", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.933], "y": [0] }, + "t": 0, + "s": [0] + }, + { "t": 60, "s": [180] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [308.5, 333, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0.011, 34.469], + [34.47, -0.012], + [-0.011, -34.469], + [-34.47, 0.011] + ], + "o": [ + [-0.012, -34.47], + [-34.469, 0.011], + [0.012, 34.47], + [34.469, -0.012] + ], + "v": [ + [62.333, 0.005], + [-0.101, -62.387], + [-62.493, 0.047], + [-0.059, 62.439] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -68.917], + [68.916, 0], + [0, 68.916], + [-68.916, 0] + ], + "o": [ + [0, 68.916], + [-68.916, 0], + [0, -68.917], + [68.916, 0] + ], + "v": [ + [124.725, 0.005], + [-0.059, 124.789], + [-124.843, 0.005], + [-0.059, -124.779] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [15.804, -1.372], + [1.206, -4.575], + [0, 0], + [18.676, -9.234], + [6.656, -4.451], + [23.252, 6.364], + [0, 0], + [0.832, -1.206], + [6.697, -14.392], + [-3.327, -3.328], + [0, 0], + [1.331, -20.755], + [-0.499, -8.062], + [17.095, -16.887], + [0, 0], + [-0.582, -1.289], + [-9.151, -12.977], + [-4.575, 1.248], + [0, 0], + [-17.345, -11.563], + [-7.196, -3.578], + [-6.114, -23.293], + [0, 0], + [-1.497, -0.124], + [-15.804, 1.373], + [-1.207, 4.575], + [0, 0], + [-18.676, 9.234], + [-6.655, 4.451], + [-23.251, -6.364], + [0, 0], + [-0.832, 1.206], + [-6.739, 14.392], + [3.327, 3.328], + [0, 0], + [-1.331, 20.755], + [0.499, 8.061], + [-17.095, 16.887], + [0, 0], + [0.582, 1.289], + [9.144, 12.981], + [4.576, -1.248], + [0, 0], + [17.345, 11.563], + [7.202, 3.563], + [6.114, 23.293], + [0, 0], + [1.498, 0.125] + ], + "o": [ + [-1.497, 0.125], + [0, 0], + [-6.114, 23.334], + [-7.196, 3.578], + [-17.345, 11.563], + [0, 0], + [-4.534, -1.248], + [-9.151, 12.978], + [-0.582, 1.289], + [0, 0], + [17.095, 16.887], + [-0.5, 8.062], + [1.331, 20.755], + [0, 0], + [-3.369, 3.328], + [6.738, 14.392], + [0.832, 1.248], + [0, 0], + [23.293, -6.364], + [6.697, 4.451], + [18.593, 9.234], + [0, 0], + [1.206, 4.533], + [15.804, 1.373], + [1.498, -0.124], + [0, 0], + [6.114, -23.335], + [7.196, -3.578], + [17.345, -11.563], + [0, 0], + [4.534, 1.206], + [9.151, -13.019], + [0.582, -1.289], + [0, 0], + [-17.095, -16.887], + [0.499, -8.062], + [-1.331, -20.756], + [0, 0], + [3.369, -3.328], + [-6.715, -14.39], + [-0.832, -1.248], + [0, 0], + [-23.293, 6.364], + [-6.676, -4.47], + [-18.592, -9.234], + [0, 0], + [-1.207, -4.575], + [-15.804, -1.372] + ], + "v": [ + [-23.81, -269.32], + [-29.508, -263.247], + [-41.529, -217.285], + [-83.04, -167.829], + [-103.838, -155.766], + [-167.436, -144.536], + [-213.314, -157.139], + [-221.425, -155.267], + [-245.259, -114.089], + [-242.805, -106.185], + [-208.905, -72.66], + [-186.86, -12.099], + [-186.86, 12.109], + [-208.905, 72.671], + [-242.805, 106.195], + [-245.259, 114.099], + [-221.425, 155.236], + [-213.314, 157.15], + [-167.477, 144.547], + [-103.838, 155.777], + [-82.999, 167.839], + [-41.529, 217.295], + [-29.508, 263.257], + [-23.81, 269.329], + [23.691, 269.329], + [29.39, 263.257], + [41.411, 217.295], + [82.922, 167.839], + [103.719, 155.777], + [167.317, 144.547], + [213.196, 157.15], + [221.307, 155.277], + [245.141, 114.099], + [242.687, 106.195], + [208.787, 72.671], + [186.742, 12.109], + [186.742, -12.098], + [208.787, -72.66], + [242.687, -106.185], + [245.141, -114.089], + [221.307, -155.225], + [213.196, -157.139], + [167.359, -144.536], + [103.719, -155.766], + [82.88, -167.828], + [41.411, -217.285], + [29.39, -263.247], + [23.691, -269.32] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [-9.816, 0], + [-9.697, -0.846], + [-7.071, -26.828], + [0, 0], + [-5.532, -2.745], + [-8.902, -5.948], + [-2.745, 0.79], + [0, 0], + [-17.802, -25.331], + [-8.236, -17.72], + [19.758, -19.508], + [0, 0], + [-0.416, -6.114], + [0.666, -10.69], + [-2.079, -1.997], + [0, 0], + [13.102, -28.076], + [11.232, -15.979], + [26.787, 7.363], + [0, 0], + [5.117, -3.411], + [9.602, -4.741], + [0.707, -2.745], + [0, 0], + [30.904, -2.663], + [19.401, 1.705], + [7.03, 26.87], + [0, 0], + [5.532, 2.745], + [8.891, 5.969], + [2.746, -0.79], + [0, 0], + [17.802, 25.332], + [8.277, 17.703], + [-19.716, 19.508], + [0, 0], + [0.416, 6.114], + [-0.666, 10.693], + [2.08, 1.997], + [0, 0], + [-13.102, 28.076], + [-11.242, 15.972], + [-26.787, -7.363], + [0, 0], + [-5.116, 3.411], + [-9.608, 4.742], + [-0.707, 2.745], + [0, 0], + [-30.905, 2.704] + ], + "o": [ + [9.733, 0.014], + [30.904, 2.662], + [0, 0], + [0.749, 2.745], + [9.609, 4.742], + [5.116, 3.411], + [0, 0], + [26.787, -7.321], + [11.231, 16.014], + [13.102, 28.076], + [0, 0], + [-2.038, 1.997], + [0.666, 10.69], + [-0.416, 6.114], + [0, 0], + [19.758, 19.508], + [-8.252, 17.703], + [-17.802, 25.373], + [0, 0], + [-2.787, -0.79], + [-8.902, 5.952], + [-5.532, 2.745], + [0, 0], + [-6.987, 26.87], + [-19.402, 1.705], + [-30.905, -2.663], + [0, 0], + [-0.748, -2.745], + [-9.611, -4.723], + [-5.116, -3.411], + [0, 0], + [-26.786, 7.321], + [-11.208, -16.008], + [-13.102, -28.076], + [0, 0], + [2.08, -1.997], + [-0.666, -10.693], + [0.416, -6.114], + [0, 0], + [-19.716, -19.508], + [8.241, -17.709], + [17.802, -25.373], + [0, 0], + [2.787, 0.79], + [8.901, -5.948], + [5.532, -2.745], + [0, 0], + [6.988, -26.87], + [9.608, -0.832] + ], + "v": [ + [-0.059, -332.751], + [29.099, -331.462], + [89.785, -279.095], + [101.764, -233.049], + [110.582, -223.733], + [138.368, -207.676], + [150.804, -204.723], + [196.683, -217.327], + [272.385, -191.123], + [301.668, -140.459], + [286.527, -61.805], + [252.669, -28.28], + [249.009, -16.051], + [249.009, 16.061], + [252.669, 28.289], + [286.527, 61.815], + [301.668, 140.47], + [272.385, 191.09], + [196.683, 217.295], + [150.846, 204.734], + [138.367, 207.687], + [110.582, 223.743], + [101.806, 233.059], + [89.743, 279.063], + [29.099, 331.473], + [-29.217, 331.473], + [-89.862, 279.063], + [-101.883, 233.059], + [-110.701, 223.743], + [-138.486, 207.687], + [-150.923, 204.734], + [-196.802, 217.336], + [-272.504, 191.132], + [-301.786, 140.47], + [-286.687, 61.815], + [-252.788, 28.289], + [-249.127, 16.061], + [-249.127, -16.051], + [-252.788, -28.28], + [-286.687, -61.805], + [-301.786, -140.459], + [-272.504, -191.08], + [-196.801, -217.285], + [-150.964, -204.723], + [-138.486, -207.676], + [-110.701, -223.733], + [-101.924, -233.049], + [-89.862, -279.053], + [-29.217, -331.504] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [0.898460657456, 0.889815326765, 0.896223060758, 1], + "ix": 4 + }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [308.077, 333.249], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 8, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ] + } + ], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 0, + "nm": "estrela Outlines 2 Comp 1", + "refId": "comp_0", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [450, 450, 0], "ix": 1 }, + "s": { + "a": 1, + "k": [ + { + "i": { "x": [0.667, 0.667, 0.667], "y": [1, 1, 1] }, + "o": { "x": [1, 1, 0.333], "y": [0, 0, 0] }, + "t": 0, + "s": [100, 100, 100] + }, + { + "i": { "x": [0, 0, 0.667], "y": [1, 1, 1] }, + "o": { "x": [0.333, 0.333, 0.333], "y": [0, 0, 0] }, + "t": 30, + "s": [80, 80, 100] + }, + { "t": 60, "s": [100, 100, 100] } + ], + "ix": 6 + } + }, + "ao": 0, + "hasMask": true, + "masksProperties": [ + { + "inv": false, + "mode": "a", + "pt": { + "a": 0, + "k": { + "i": [ + [89.47, 0], + [0, -89.47], + [-89.47, 0], + [0, 89.47] + ], + "o": [ + [-89.47, 0], + [0, 89.47], + [89.47, 0], + [0, -89.47] + ], + "v": [ + [446, 286], + [284, 448], + [446, 610], + [608, 448] + ], + "c": true + }, + "ix": 1 + }, + "o": { "a": 0, "k": 100, "ix": 3 }, + "x": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Mask 1" + } + ], + "w": 900, + "h": 900, + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "estrela Outlines", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.84], "y": [-1.753] }, + "o": { "x": [0], "y": [0] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.361], "y": [1] }, + "o": { "x": [0], "y": [0] }, + "t": 28, + "s": [15] + }, + { "t": 60, "s": [180] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [450, 450, 0], "ix": 2 }, + "a": { "a": 0, "k": [308.5, 333, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0.011, 34.469], + [34.47, -0.012], + [-0.011, -34.469], + [-34.47, 0.011] + ], + "o": [ + [-0.012, -34.47], + [-34.469, 0.011], + [0.012, 34.47], + [34.469, -0.012] + ], + "v": [ + [62.333, 0.005], + [-0.101, -62.387], + [-62.493, 0.047], + [-0.059, 62.439] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [0, -68.917], + [68.916, 0], + [0, 68.916], + [-68.916, 0] + ], + "o": [ + [0, 68.916], + [-68.916, 0], + [0, -68.917], + [68.916, 0] + ], + "v": [ + [124.725, 0.005], + [-0.059, 124.789], + [-124.843, 0.005], + [-0.059, -124.779] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [15.804, -1.372], + [1.206, -4.575], + [0, 0], + [18.676, -9.234], + [6.656, -4.451], + [23.252, 6.364], + [0, 0], + [0.832, -1.206], + [6.697, -14.392], + [-3.327, -3.328], + [0, 0], + [1.331, -20.755], + [-0.499, -8.062], + [17.095, -16.887], + [0, 0], + [-0.582, -1.289], + [-9.151, -12.977], + [-4.575, 1.248], + [0, 0], + [-17.345, -11.563], + [-7.196, -3.578], + [-6.114, -23.293], + [0, 0], + [-1.497, -0.124], + [-15.804, 1.373], + [-1.207, 4.575], + [0, 0], + [-18.676, 9.234], + [-6.655, 4.451], + [-23.251, -6.364], + [0, 0], + [-0.832, 1.206], + [-6.739, 14.392], + [3.327, 3.328], + [0, 0], + [-1.331, 20.755], + [0.499, 8.061], + [-17.095, 16.887], + [0, 0], + [0.582, 1.289], + [9.144, 12.981], + [4.576, -1.248], + [0, 0], + [17.345, 11.563], + [7.202, 3.563], + [6.114, 23.293], + [0, 0], + [1.498, 0.125] + ], + "o": [ + [-1.497, 0.125], + [0, 0], + [-6.114, 23.334], + [-7.196, 3.578], + [-17.345, 11.563], + [0, 0], + [-4.534, -1.248], + [-9.151, 12.978], + [-0.582, 1.289], + [0, 0], + [17.095, 16.887], + [-0.5, 8.062], + [1.331, 20.755], + [0, 0], + [-3.369, 3.328], + [6.738, 14.392], + [0.832, 1.248], + [0, 0], + [23.293, -6.364], + [6.697, 4.451], + [18.593, 9.234], + [0, 0], + [1.206, 4.533], + [15.804, 1.373], + [1.498, -0.124], + [0, 0], + [6.114, -23.335], + [7.196, -3.578], + [17.345, -11.563], + [0, 0], + [4.534, 1.206], + [9.151, -13.019], + [0.582, -1.289], + [0, 0], + [-17.095, -16.887], + [0.499, -8.062], + [-1.331, -20.756], + [0, 0], + [3.369, -3.328], + [-6.715, -14.39], + [-0.832, -1.248], + [0, 0], + [-23.293, 6.364], + [-6.676, -4.47], + [-18.592, -9.234], + [0, 0], + [-1.207, -4.575], + [-15.804, -1.372] + ], + "v": [ + [-23.81, -269.32], + [-29.508, -263.247], + [-41.529, -217.285], + [-83.04, -167.829], + [-103.838, -155.766], + [-167.436, -144.536], + [-213.314, -157.139], + [-221.425, -155.267], + [-245.259, -114.089], + [-242.805, -106.185], + [-208.905, -72.66], + [-186.86, -12.099], + [-186.86, 12.109], + [-208.905, 72.671], + [-242.805, 106.195], + [-245.259, 114.099], + [-221.425, 155.236], + [-213.314, 157.15], + [-167.477, 144.547], + [-103.838, 155.777], + [-82.999, 167.839], + [-41.529, 217.295], + [-29.508, 263.257], + [-23.81, 269.329], + [23.691, 269.329], + [29.39, 263.257], + [41.411, 217.295], + [82.922, 167.839], + [103.719, 155.777], + [167.317, 144.547], + [213.196, 157.15], + [221.307, 155.277], + [245.141, 114.099], + [242.687, 106.195], + [208.787, 72.671], + [186.742, 12.109], + [186.742, -12.098], + [208.787, -72.66], + [242.687, -106.185], + [245.141, -114.089], + [221.307, -155.225], + [213.196, -157.139], + [167.359, -144.536], + [103.719, -155.766], + [82.88, -167.828], + [41.411, -217.285], + [29.39, -263.247], + [23.691, -269.32] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [-9.816, 0], + [-9.697, -0.846], + [-7.071, -26.828], + [0, 0], + [-5.532, -2.745], + [-8.902, -5.948], + [-2.745, 0.79], + [0, 0], + [-17.802, -25.331], + [-8.236, -17.72], + [19.758, -19.508], + [0, 0], + [-0.416, -6.114], + [0.666, -10.69], + [-2.079, -1.997], + [0, 0], + [13.102, -28.076], + [11.232, -15.979], + [26.787, 7.363], + [0, 0], + [5.117, -3.411], + [9.602, -4.741], + [0.707, -2.745], + [0, 0], + [30.904, -2.663], + [19.401, 1.705], + [7.03, 26.87], + [0, 0], + [5.532, 2.745], + [8.891, 5.969], + [2.746, -0.79], + [0, 0], + [17.802, 25.332], + [8.277, 17.703], + [-19.716, 19.508], + [0, 0], + [0.416, 6.114], + [-0.666, 10.693], + [2.08, 1.997], + [0, 0], + [-13.102, 28.076], + [-11.242, 15.972], + [-26.787, -7.363], + [0, 0], + [-5.116, 3.411], + [-9.608, 4.742], + [-0.707, 2.745], + [0, 0], + [-30.905, 2.704] + ], + "o": [ + [9.733, 0.014], + [30.904, 2.662], + [0, 0], + [0.749, 2.745], + [9.609, 4.742], + [5.116, 3.411], + [0, 0], + [26.787, -7.321], + [11.231, 16.014], + [13.102, 28.076], + [0, 0], + [-2.038, 1.997], + [0.666, 10.69], + [-0.416, 6.114], + [0, 0], + [19.758, 19.508], + [-8.252, 17.703], + [-17.802, 25.373], + [0, 0], + [-2.787, -0.79], + [-8.902, 5.952], + [-5.532, 2.745], + [0, 0], + [-6.987, 26.87], + [-19.402, 1.705], + [-30.905, -2.663], + [0, 0], + [-0.748, -2.745], + [-9.611, -4.723], + [-5.116, -3.411], + [0, 0], + [-26.786, 7.321], + [-11.208, -16.008], + [-13.102, -28.076], + [0, 0], + [2.08, -1.997], + [-0.666, -10.693], + [0.416, -6.114], + [0, 0], + [-19.716, -19.508], + [8.241, -17.709], + [17.802, -25.373], + [0, 0], + [2.787, 0.79], + [8.901, -5.948], + [5.532, -2.745], + [0, 0], + [6.988, -26.87], + [9.608, -0.832] + ], + "v": [ + [-0.059, -332.751], + [29.099, -331.462], + [89.785, -279.095], + [101.764, -233.049], + [110.582, -223.733], + [138.368, -207.676], + [150.804, -204.723], + [196.683, -217.327], + [272.385, -191.123], + [301.668, -140.459], + [286.527, -61.805], + [252.669, -28.28], + [249.009, -16.051], + [249.009, 16.061], + [252.669, 28.289], + [286.527, 61.815], + [301.668, 140.47], + [272.385, 191.09], + [196.683, 217.295], + [150.846, 204.734], + [138.367, 207.687], + [110.582, 223.743], + [101.806, 233.059], + [89.743, 279.063], + [29.099, 331.473], + [-29.217, 331.473], + [-89.862, 279.063], + [-101.883, 233.059], + [-110.701, 223.743], + [-138.486, 207.687], + [-150.923, 204.734], + [-196.802, 217.336], + [-272.504, 191.132], + [-301.786, 140.47], + [-286.687, 61.815], + [-252.788, 28.289], + [-249.127, 16.061], + [-249.127, -16.051], + [-252.788, -28.28], + [-286.687, -61.805], + [-301.786, -140.459], + [-272.504, -191.08], + [-196.801, -217.285], + [-150.964, -204.723], + [-138.486, -207.676], + [-110.701, -223.733], + [-101.924, -233.049], + [-89.862, -279.053], + [-29.217, -331.504] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [0.898460657456, 0.889815326765, 0.896223060758, 1], + "ix": 4 + }, + "o": { "a": 0, "k": 100, "ix": 5 }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [308.077, 333.249], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 8, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/src/renderer/src/assets/lottie/stars.json b/src/renderer/src/assets/lottie/stars.json new file mode 100644 index 00000000..ae72f2e9 --- /dev/null +++ b/src/renderer/src/assets/lottie/stars.json @@ -0,0 +1,928 @@ +{ + "v": "4.8.0", + "meta": { "g": "LottieFiles AE 3.5.6", "a": "", "k": "", "d": "", "tc": "" }, + "fr": 60, + "ip": 0, + "op": 120, + "w": 300, + "h": 300, + "nm": "Comp 1", + "ddd": 0, + "assets": [ + { + "id": "comp_0", + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 5, + "nm": "3", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 30, + "s": [8] + }, + { "t": 60, "s": [0] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [930, 525, 0], "ix": 2 }, + "a": { "a": 0, "k": [16.605, -23.904, 0], "ix": 1 }, + "s": { "a": 0, "k": [170, 170, 100], "ix": 6 } + }, + "ao": 0, + "hasMask": true, + "masksProperties": [ + { + "inv": false, + "mode": "a", + "pt": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [14.987, -34.426], + [9.105, -30.309], + [9.987, -22.073], + [17.487, -16.779], + [24.105, -23.544], + [22.193, -30.603] + ], + "c": true + }, + "ix": 1 + }, + "o": { "a": 0, "k": 100, "ix": 3 }, + "x": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Mask 1" + } + ], + "ef": [ + { + "ty": 21, + "nm": "Fill", + "np": 9, + "mn": "ADBE Fill", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 10, + "nm": "Fill Mask", + "mn": "ADBE Fill-0001", + "ix": 1, + "v": { "a": 0, "k": 0, "ix": 1 } + }, + { + "ty": 7, + "nm": "All Masks", + "mn": "ADBE Fill-0007", + "ix": 2, + "v": { "a": 0, "k": 0, "ix": 2 } + }, + { + "ty": 2, + "nm": "Color", + "mn": "ADBE Fill-0002", + "ix": 3, + "v": { + "a": 0, + "k": [0.992156863213, 0.880375564098, 0.128396704793, 1], + "ix": 3 + } + }, + { + "ty": 7, + "nm": "Invert", + "mn": "ADBE Fill-0006", + "ix": 4, + "v": { "a": 0, "k": 0, "ix": 4 } + }, + { + "ty": 0, + "nm": "Horizontal Feather", + "mn": "ADBE Fill-0003", + "ix": 5, + "v": { "a": 0, "k": 0, "ix": 5 } + }, + { + "ty": 0, + "nm": "Vertical Feather", + "mn": "ADBE Fill-0004", + "ix": 6, + "v": { "a": 0, "k": 0, "ix": 6 } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Fill-0005", + "ix": 7, + "v": { "a": 0, "k": 1, "ix": 7 } + } + ] + } + ], + "t": { + "d": { + "k": [ + { + "s": { + "s": 40, + "f": "SegoeUIEmoji", + "t": "✨", + "j": 0, + "tr": 0, + "lh": 48, + "ls": 0, + "fc": [1, 1, 1] + }, + "t": 0 + } + ] + }, + "p": {}, + "m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } }, + "a": [] + }, + "ip": 0, + "op": 123, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 5, + "nm": "2", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 30, + "s": [-8] + }, + { "t": 60, "s": [0] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [960, 540, 0], "ix": 2 }, + "a": { "a": 0, "k": [31.912, -13.397, 0], "ix": 1 }, + "s": { "a": 0, "k": [170, 170, 100], "ix": 6 } + }, + "ao": 0, + "hasMask": true, + "masksProperties": [ + { + "inv": false, + "mode": "a", + "pt": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [31.31, -34.72], + [24.546, -22.514], + [16.605, -16.485], + [17.046, -11.338], + [21.163, -7.073], + [27.487, -0.309], + [33.663, 10.133], + [47.634, -1.926], + [51.31, -12.073] + ], + "c": true + }, + "ix": 1 + }, + "o": { "a": 0, "k": 100, "ix": 3 }, + "x": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Mask 1" + } + ], + "ef": [ + { + "ty": 21, + "nm": "Fill", + "np": 9, + "mn": "ADBE Fill", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 10, + "nm": "Fill Mask", + "mn": "ADBE Fill-0001", + "ix": 1, + "v": { "a": 0, "k": 0, "ix": 1 } + }, + { + "ty": 7, + "nm": "All Masks", + "mn": "ADBE Fill-0007", + "ix": 2, + "v": { "a": 0, "k": 0, "ix": 2 } + }, + { + "ty": 2, + "nm": "Color", + "mn": "ADBE Fill-0002", + "ix": 3, + "v": { + "a": 0, + "k": [0.992156863213, 0.880375564098, 0.128396704793, 1], + "ix": 3 + } + }, + { + "ty": 7, + "nm": "Invert", + "mn": "ADBE Fill-0006", + "ix": 4, + "v": { "a": 0, "k": 0, "ix": 4 } + }, + { + "ty": 0, + "nm": "Horizontal Feather", + "mn": "ADBE Fill-0003", + "ix": 5, + "v": { "a": 0, "k": 0, "ix": 5 } + }, + { + "ty": 0, + "nm": "Vertical Feather", + "mn": "ADBE Fill-0004", + "ix": 6, + "v": { "a": 0, "k": 0, "ix": 6 } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Fill-0005", + "ix": 7, + "v": { "a": 0, "k": 1, "ix": 7 } + } + ] + } + ], + "t": { + "d": { + "k": [ + { + "s": { + "s": 40, + "f": "SegoeUIEmoji", + "t": "✨", + "j": 0, + "tr": 0, + "lh": 48, + "ls": 0, + "fc": [1, 1, 1] + }, + "t": 0 + } + ] + }, + "p": {}, + "m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } }, + "a": [] + }, + "ip": 0, + "op": 123, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 5, + "nm": "✨", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.055], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 30, + "s": [8] + }, + { "t": 60, "s": [0] } + ], + "ix": 10 + }, + "p": { "a": 0, "k": [935, 560, 0], "ix": 2 }, + "a": { "a": 0, "k": [14.973, -6.64, 0], "ix": 1 }, + "s": { "a": 0, "k": [170, 170, 100], "ix": 6 } + }, + "ao": 0, + "hasMask": true, + "masksProperties": [ + { + "inv": false, + "mode": "a", + "pt": { + "a": 0, + "k": { + "i": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "o": [ + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ], + "v": [ + [13.957, -17.514], + [2.928, -9.132], + [2.487, 1.603], + [14.105, 7.339], + [21.605, -0.161], + [22.193, -5.161], + [17.34, -10.014] + ], + "c": true + }, + "ix": 1 + }, + "o": { "a": 0, "k": 100, "ix": 3 }, + "x": { "a": 0, "k": 0, "ix": 4 }, + "nm": "Mask 1" + } + ], + "ef": [ + { + "ty": 21, + "nm": "Fill", + "np": 9, + "mn": "ADBE Fill", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 10, + "nm": "Fill Mask", + "mn": "ADBE Fill-0001", + "ix": 1, + "v": { "a": 0, "k": 0, "ix": 1 } + }, + { + "ty": 7, + "nm": "All Masks", + "mn": "ADBE Fill-0007", + "ix": 2, + "v": { "a": 0, "k": 0, "ix": 2 } + }, + { + "ty": 2, + "nm": "Color", + "mn": "ADBE Fill-0002", + "ix": 3, + "v": { + "a": 0, + "k": [0.992156863213, 0.880375564098, 0.128396704793, 1], + "ix": 3 + } + }, + { + "ty": 7, + "nm": "Invert", + "mn": "ADBE Fill-0006", + "ix": 4, + "v": { "a": 0, "k": 0, "ix": 4 } + }, + { + "ty": 0, + "nm": "Horizontal Feather", + "mn": "ADBE Fill-0003", + "ix": 5, + "v": { "a": 0, "k": 0, "ix": 5 } + }, + { + "ty": 0, + "nm": "Vertical Feather", + "mn": "ADBE Fill-0004", + "ix": 6, + "v": { "a": 0, "k": 0, "ix": 6 } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Fill-0005", + "ix": 7, + "v": { "a": 0, "k": 1, "ix": 7 } + } + ] + } + ], + "t": { + "d": { + "k": [ + { + "s": { + "s": 40, + "f": "SegoeUIEmoji", + "t": "✨", + "j": 0, + "tr": 0, + "lh": 48, + "ls": 0, + "fc": [1, 1, 1] + }, + "t": 0 + } + ] + }, + "p": {}, + "m": { "g": 1, "a": { "a": 0, "k": [0, 0], "ix": 2 } }, + "a": [] + }, + "ip": 0, + "op": 123, + "st": 0, + "bm": 0 + } + ] + } + ], + "fonts": { + "list": [ + { + "fName": "SegoeUIEmoji", + "fFamily": "Segoe UI Emoji", + "fStyle": "Regular", + "ascent": 74.0234375 + } + ] + }, + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 0, + "nm": "botão", + "refId": "comp_0", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [155, 154, 0], "ix": 2 }, + "a": { "a": 0, "k": [960, 540, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100, 100], "ix": 6 } + }, + "ao": 0, + "ef": [ + { + "ty": 25, + "nm": "Drop Shadow", + "np": 8, + "mn": "ADBE Drop Shadow", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 2, + "nm": "Shadow Color", + "mn": "ADBE Drop Shadow-0001", + "ix": 1, + "v": { + "a": 0, + "k": [1, 0.829733371735, 0.414901971817, 1], + "ix": 1 + } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Drop Shadow-0002", + "ix": 2, + "v": { + "a": 1, + "k": [ + { + "i": { "x": [0], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 0, + "s": [127.5] + }, + { + "i": { "x": [0], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 15, + "s": [204] + }, + { + "i": { "x": [0], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 30, + "s": [127.5] + }, + { + "i": { "x": [0], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "t": 45, + "s": [204] + }, + { "t": 70, "s": [76.5] } + ], + "ix": 2 + } + }, + { + "ty": 0, + "nm": "Direction", + "mn": "ADBE Drop Shadow-0003", + "ix": 3, + "v": { "a": 0, "k": 135, "ix": 3 } + }, + { + "ty": 0, + "nm": "Distance", + "mn": "ADBE Drop Shadow-0004", + "ix": 4, + "v": { "a": 0, "k": 0, "ix": 4 } + }, + { + "ty": 0, + "nm": "Softness", + "mn": "ADBE Drop Shadow-0005", + "ix": 5, + "v": { "a": 0, "k": 40, "ix": 5 } + }, + { + "ty": 7, + "nm": "Shadow Only", + "mn": "ADBE Drop Shadow-0006", + "ix": 6, + "v": { "a": 0, "k": 0, "ix": 6 } + } + ] + } + ], + "w": 1920, + "h": 1080, + "ip": 0, + "op": 120, + "st": 0, + "bm": 0 + } + ], + "markers": [], + "chars": [ + { + "ch": "✨", + "size": 40, + "style": "Regular", + "w": 137.3, + "data": { + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [0.423, 1.042], + [0, 0], + [0.7, 0], + [0.293, -0.618], + [0, 0], + [1.041, -0.488], + [0, 0], + [0, -0.684], + [-0.652, -0.293], + [0, 0], + [-0.423, -1.041], + [0, 0], + [-0.716, 0], + [-0.293, 0.619], + [0, 0], + [-1.042, 0.488], + [0, 0], + [0, 0.684], + [0.618, 0.293], + [0, 0] + ], + "o": [ + [0, 0], + [-0.326, -0.618], + [-0.7, 0], + [0, 0], + [-0.456, 1.009], + [0, 0], + [-0.652, 0.293], + [0, 0.684], + [0, 0], + [1.074, 0.456], + [0, 0], + [0.293, 0.619], + [0.716, 0], + [0, 0], + [0.455, -1.009], + [0, 0], + [0.618, -0.293], + [0, -0.684], + [0, 0], + [-1.074, -0.455] + ], + "v": [ + [47.119, -68.994], + [43.799, -76.562], + [42.261, -77.49], + [40.771, -76.562], + [37.402, -68.994], + [35.156, -66.748], + [30.908, -64.893], + [29.932, -63.428], + [30.908, -61.963], + [35.156, -60.107], + [37.402, -57.861], + [40.771, -50.244], + [42.285, -49.316], + [43.799, -50.244], + [47.119, -57.861], + [49.365, -60.107], + [53.662, -61.963], + [54.59, -63.428], + [53.662, -64.893], + [49.365, -66.748] + ], + "c": true + }, + "ix": 2 + }, + "nm": "✨", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [1.334, 3.223], + [0, 0], + [1.204, 0.423], + [1.204, -0.423], + [0.618, -1.237], + [0, 0], + [3.125, -1.432], + [0, 0], + [0.423, -1.221], + [-0.423, -1.221], + [-1.27, -0.618], + [0, 0], + [-1.335, -3.223], + [0, 0], + [-1.205, -0.407], + [-1.205, 0.407], + [-0.619, 1.27], + [0, 0], + [-3.125, 1.433], + [0, 0], + [-0.423, 1.221], + [0.423, 1.221], + [1.27, 0.619], + [0, 0] + ], + "o": [ + [0, 0], + [-0.619, -1.237], + [-1.205, -0.423], + [-1.205, 0.423], + [0, 0], + [-1.367, 3.223], + [0, 0], + [-1.27, 0.619], + [-0.423, 1.221], + [0.423, 1.221], + [0, 0], + [3.157, 1.433], + [0, 0], + [0.618, 1.27], + [1.204, 0.407], + [1.204, -0.407], + [0, 0], + [1.367, -3.223], + [0, 0], + [1.27, -0.618], + [0.423, -1.221], + [-0.423, -1.221], + [0, 0], + [-3.158, -1.432] + ], + "v": [ + [95.605, -50.83], + [85.498, -74.658], + [82.764, -77.148], + [79.15, -77.148], + [76.416, -74.658], + [66.357, -50.83], + [59.619, -43.848], + [46.875, -38.086], + [44.336, -35.327], + [44.336, -31.665], + [46.875, -28.906], + [59.619, -23.145], + [66.357, -16.162], + [76.416, 7.666], + [79.15, 10.181], + [82.764, 10.181], + [85.498, 7.666], + [95.605, -16.162], + [102.344, -23.145], + [115.088, -28.906], + [117.627, -31.665], + [117.627, -35.327], + [115.088, -38.086], + [102.344, -43.848] + ], + "c": true + }, + "ix": 2 + }, + "nm": "✨", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [-1.367, -0.651], + [0, 0], + [0, -0.928], + [0.813, -0.423], + [0, 0], + [0.586, -1.399], + [0, 0], + [0.895, 0], + [0.391, 0.846], + [0, 0], + [1.334, 0.652], + [0, 0], + [0, 0.928], + [-0.814, 0.423], + [0, 0], + [-0.586, 1.4], + [0, 0], + [-0.896, 0], + [-0.391, -0.846], + [0, 0] + ], + "o": [ + [0, 0], + [0.813, 0.423], + [0, 0.928], + [0, 0], + [-1.335, 0.652], + [0, 0], + [-0.391, 0.846], + [-0.896, 0], + [0, 0], + [-0.586, -1.399], + [0, 0], + [-0.814, -0.423], + [0, -0.928], + [0, 0], + [1.334, -0.651], + [0, 0], + [0.391, -0.846], + [0.895, 0], + [0, 0], + [0.553, 1.4] + ], + "v": [ + [44.385, -16.943], + [49.854, -14.404], + [51.074, -12.378], + [49.854, -10.352], + [44.385, -7.812], + [41.504, -4.736], + [37.158, 5.713], + [35.229, 6.982], + [33.301, 5.713], + [28.955, -4.736], + [26.074, -7.812], + [20.605, -10.352], + [19.385, -12.378], + [20.605, -14.404], + [26.074, -16.943], + [28.955, -20.02], + [33.301, -30.469], + [35.229, -31.738], + [37.158, -30.469], + [41.504, -20.02] + ], + "c": true + }, + "ix": 2 + }, + "nm": "✨", + "mn": "ADBE Vector Shape - Group", + "hd": false + } + ], + "nm": "✨", + "np": 6, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ] + }, + "fFamily": "Segoe UI Emoji" + } + ] +} diff --git a/src/renderer/src/assets/steam-logo.svg b/src/renderer/src/assets/steam-logo.svg new file mode 100644 index 00000000..1e5dd9be --- /dev/null +++ b/src/renderer/src/assets/steam-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/assets/x-icon.svg b/src/renderer/src/assets/x-icon.svg new file mode 100644 index 00000000..f594427b --- /dev/null +++ b/src/renderer/src/assets/x-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/components/async-image/async-image.tsx b/src/renderer/src/components/async-image/async-image.tsx new file mode 100644 index 00000000..857a9942 --- /dev/null +++ b/src/renderer/src/components/async-image/async-image.tsx @@ -0,0 +1,27 @@ +import { forwardRef, useEffect, useState } from "react"; + +export interface AsyncImageProps + extends React.DetailedHTMLProps< + React.ImgHTMLAttributes, + HTMLImageElement + > { + onSettled?: (url: string) => void; +} + +export const AsyncImage = forwardRef( + ({ onSettled, ...props }, ref) => { + const [source, setSource] = useState(null); + + useEffect(() => { + if (props.src && props.src.startsWith("http")) { + window.electron.getOrCacheImage(props.src).then((url) => { + setSource(url); + + if (onSettled) onSettled(url); + }); + } + }, [props.src, onSettled]); + + return ; + } +); diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.css.ts b/src/renderer/src/components/bottom-panel/bottom-panel.css.ts new file mode 100644 index 00000000..f251d1c0 --- /dev/null +++ b/src/renderer/src/components/bottom-panel/bottom-panel.css.ts @@ -0,0 +1,23 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const bottomPanel = style({ + width: "100%", + borderTop: `solid 1px ${vars.color.borderColor}`, + padding: `${SPACING_UNIT / 2}px ${SPACING_UNIT * 2}px`, + display: "flex", + alignItems: "center", + transition: "all ease 0.2s", + justifyContent: "space-between", + fontSize: vars.size.bodyFontSize, + zIndex: "1", +}); + +export const downloadsButton = style({ + color: vars.color.bodyText, + borderBottom: "1px solid transparent", + ":hover": { + borderBottom: `1px solid ${vars.color.bodyText}`, + cursor: "pointer", + }, +}); diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.tsx b/src/renderer/src/components/bottom-panel/bottom-panel.tsx new file mode 100644 index 00000000..df1f49cb --- /dev/null +++ b/src/renderer/src/components/bottom-panel/bottom-panel.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from "react-i18next"; + +import { useDownload } from "@renderer/hooks"; + +import * as styles from "./bottom-panel.css"; +import { vars } from "../../theme.css"; +import { useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { VERSION_CODENAME } from "@renderer/constants"; + +export function BottomPanel() { + const { t } = useTranslation("bottom_panel"); + + const navigate = useNavigate(); + + const { game, progress, downloadSpeed, eta, isDownloading } = useDownload(); + + const [version, setVersion] = useState(""); + + useEffect(() => { + window.electron.getVersion().then((result) => setVersion(result)); + }, []); + + const status = useMemo(() => { + if (isDownloading) { + if (game.status === "downloading_metadata") + return t("downloading_metadata", { title: game.title }); + + if (game.status === "checking_files") + return t("checking_files", { + title: game.title, + percentage: progress, + }); + + return t("downloading", { + title: game?.title, + percentage: progress, + eta, + speed: downloadSpeed, + }); + } + + return t("no_downloads_in_progress"); + }, [t, game, progress, eta, isDownloading, downloadSpeed]); + + return ( +
+ + + + v{version} "{VERSION_CODENAME}" + +
+ ); +} diff --git a/src/renderer/src/components/button/button.css.ts b/src/renderer/src/components/button/button.css.ts new file mode 100644 index 00000000..18866bc0 --- /dev/null +++ b/src/renderer/src/components/button/button.css.ts @@ -0,0 +1,52 @@ +import { style, styleVariants } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +const base = style({ + padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`, + backgroundColor: "#c0c1c7", + borderRadius: "8px", + border: "solid 1px transparent", + transition: "all ease 0.2s", + cursor: "pointer", + minHeight: "40px", + display: "flex", + alignItems: "center", + justifyContent: "center", + gap: `${SPACING_UNIT}px`, + ":active": { + opacity: vars.opacity.active, + }, + ":disabled": { + opacity: vars.opacity.disabled, + pointerEvents: "none", + }, +}); + +export const button = styleVariants({ + primary: [ + base, + { + ":hover": { + backgroundColor: "#DADBE1", + }, + }, + ], + outline: [ + base, + { + backgroundColor: "transparent", + border: "solid 1px #c0c1c7", + color: "#c0c1c7", + ":hover": { + backgroundColor: "rgba(255, 255, 255, 0.1)", + }, + }, + ], + dark: [ + base, + { + backgroundColor: vars.color.darkBackground, + color: "#c0c1c7", + }, + ], +}); diff --git a/src/renderer/src/components/button/button.tsx b/src/renderer/src/components/button/button.tsx new file mode 100644 index 00000000..41b58367 --- /dev/null +++ b/src/renderer/src/components/button/button.tsx @@ -0,0 +1,27 @@ +import cn from "classnames"; +import * as styles from "./button.css"; + +export interface ButtonProps + extends React.DetailedHTMLProps< + React.ButtonHTMLAttributes, + HTMLButtonElement + > { + theme?: keyof typeof styles.button; +} + +export function Button({ + children, + theme = "primary", + className, + ...props +}: ButtonProps) { + return ( + + ); +} diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts new file mode 100644 index 00000000..2b7cb77c --- /dev/null +++ b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts @@ -0,0 +1,40 @@ +import { SPACING_UNIT, vars } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; + +export const checkboxField = style({ + display: "flex", + flexDirection: "row", + alignItems: "center", + gap: `${SPACING_UNIT}px`, + cursor: "pointer", +}); + +export const checkbox = style({ + width: "20px", + height: "20px", + borderRadius: "4px", + backgroundColor: vars.color.darkBackground, + display: "flex", + justifyContent: "center", + alignItems: "center", + position: "relative", + transition: "all ease 0.2s", + border: `solid 1px ${vars.color.borderColor}`, + ":hover": { + borderColor: "rgba(255, 255, 255, 0.5)", + }, +}); + +export const checkboxInput = style({ + width: "100%", + height: "100%", + position: "absolute", + margin: "0", + padding: "0", + opacity: "0", + cursor: "pointer", +}); + +export const checkboxLabel = style({ + cursor: "pointer", +}); diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.tsx b/src/renderer/src/components/checkbox-field/checkbox-field.tsx new file mode 100644 index 00000000..bb81a910 --- /dev/null +++ b/src/renderer/src/components/checkbox-field/checkbox-field.tsx @@ -0,0 +1,32 @@ +import { useId } from "react"; +import * as styles from "./checkbox-field.css"; +import { CheckIcon } from "@primer/octicons-react"; + +export interface CheckboxFieldProps + extends React.DetailedHTMLProps< + React.InputHTMLAttributes, + HTMLInputElement + > { + label: string; +} + +export function CheckboxField({ label, ...props }: CheckboxFieldProps) { + const id = useId(); + + return ( +
+
+ + {props.checked && } +
+ +
+ ); +} diff --git a/src/renderer/src/components/game-card/game-card.css.ts b/src/renderer/src/components/game-card/game-card.css.ts new file mode 100644 index 00000000..f8d835fb --- /dev/null +++ b/src/renderer/src/components/game-card/game-card.css.ts @@ -0,0 +1,127 @@ +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const card = recipe({ + base: { + width: "100%", + height: "180px", + boxShadow: "0px 0px 15px 0px #000000", + overflow: "hidden", + borderRadius: "4px", + transition: "all ease 0.2s", + border: `solid 1px ${vars.color.borderColor}`, + cursor: "pointer", + zIndex: "1", + ":active": { + opacity: vars.opacity.active, + }, + }, + variants: { + disabled: { + true: { + pointerEvents: "none", + boxShadow: "none", + opacity: vars.opacity.disabled, + filter: "grayscale(50%)", + }, + }, + }, +}); + +export const backdrop = style({ + background: "linear-gradient(0deg, rgba(0, 0, 0, 0.7) 50%, transparent 100%)", + width: "100%", + height: "100%", + display: "flex", + justifyContent: "flex-end", + flexDirection: "column", + position: "relative", +}); + +export const cover = style({ + width: "100%", + height: "100%", + objectFit: "cover", + objectPosition: "center", + position: "absolute", + zIndex: "-1", + transition: "all ease 0.2s", + selectors: { + [`${card({})}:hover &`]: { + transform: "scale(1.05)", + }, + }, +}); + +export const content = style({ + color: "#DADBE1", + padding: `${SPACING_UNIT}px ${SPACING_UNIT * 2}px`, + display: "flex", + alignItems: "flex-start", + gap: `${SPACING_UNIT}px`, + flexDirection: "column", + transition: "all ease 0.2s", + transform: "translateY(24px)", + selectors: { + [`${card({})}:hover &`]: { + transform: "translateY(0px)", + }, + }, +}); + +export const title = style({ + fontSize: "16px", + fontWeight: "bold", + textAlign: "left", +}); + +export const downloadOptions = style({ + display: "flex", + margin: "0", + padding: "0", + gap: `${SPACING_UNIT}px`, + flexWrap: "wrap", +}); + +export const downloadOption = style({ + color: "#c0c1c7", + fontSize: "10px", + padding: `${SPACING_UNIT / 2}px ${SPACING_UNIT}px`, + border: "solid 1px #c0c1c7", + borderRadius: "4px", + display: "flex", + alignItems: "center", +}); + +export const specifics = style({ + display: "flex", + gap: `${SPACING_UNIT * 2}px`, + justifyContent: "center", +}); + +export const specificsItem = style({ + gap: `${SPACING_UNIT}px`, + display: "flex", + color: "#c0c1c7", + fontSize: "12px", + alignItems: "flex-end", +}); + +export const titleContainer = style({ + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT}px`, + color: "#c0c1c7", +}); + +export const shopIcon = style({ + width: "20px", + height: "20px", + minWidth: "20px", +}); + +export const noDownloadsLabel = style({ + color: vars.color.bodyText, + fontWeight: "bold", +}); diff --git a/src/renderer/src/components/game-card/game-card.tsx b/src/renderer/src/components/game-card/game-card.tsx new file mode 100644 index 00000000..ce9789c8 --- /dev/null +++ b/src/renderer/src/components/game-card/game-card.tsx @@ -0,0 +1,87 @@ +import { DownloadIcon, FileDirectoryIcon } from "@primer/octicons-react"; +import type { CatalogueEntry } from "@types"; + +import SteamLogo from "@renderer/assets/steam-logo.svg?react"; +import EpicGamesLogo from "@renderer/assets/epic-games-logo.svg?react"; + +import { AsyncImage } from "../async-image/async-image"; + +import * as styles from "./game-card.css"; +import { useAppSelector } from "@renderer/hooks"; +import { useTranslation } from "react-i18next"; + +export interface GameCardProps + extends React.DetailedHTMLProps< + React.ButtonHTMLAttributes, + HTMLButtonElement + > { + game: CatalogueEntry; + disabled?: boolean; +} + +const shopIcon = { + epic: , + steam: , +}; + +export function GameCard({ game, disabled, ...props }: GameCardProps) { + const { t } = useTranslation("game_card"); + + const repackersFriendlyNames = useAppSelector( + (state) => state.repackersFriendlyNames.value + ); + + const uniqueRepackers = Array.from( + new Set(game.repacks.map(({ repacker }) => repacker)) + ); + + return ( + + ); +} diff --git a/src/renderer/src/components/header/header.css.ts b/src/renderer/src/components/header/header.css.ts new file mode 100644 index 00000000..eb95dc6e --- /dev/null +++ b/src/renderer/src/components/header/header.css.ts @@ -0,0 +1,148 @@ +import type { ComplexStyleRule } from "@vanilla-extract/css"; +import { keyframes, style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; + +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const slideIn = keyframes({ + "0%": { transform: "translateX(20px)", opacity: "0" }, + "100%": { + transform: "translateX(0)", + opacity: "1", + }, +}); + +export const slideOut = keyframes({ + "0%": { transform: "translateX(0px)", opacity: "1" }, + "100%": { + transform: "translateX(20px)", + opacity: "0", + }, +}); + +export const header = recipe({ + base: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + gap: `${SPACING_UNIT * 2}px`, + WebkitAppRegion: "drag", + width: "100%", + padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`, + color: "#c0c1c7", + borderBottom: `solid 1px ${vars.color.borderColor}`, + backgroundColor: vars.color.darkBackground, + } as ComplexStyleRule, + variants: { + draggingDisabled: { + true: { + WebkitAppRegion: "no-drag", + } as ComplexStyleRule, + }, + isWindows: { + true: { + WebkitAppRegion: "no-drag", + } as ComplexStyleRule, + }, + }, +}); + +export const search = recipe({ + base: { + backgroundColor: vars.color.background, + display: "inline-flex", + transition: "all ease 0.2s", + width: "200px", + alignItems: "center", + borderRadius: "8px", + border: `solid 1px ${vars.color.borderColor}`, + height: "40px", + WebkitAppRegion: "no-drag", + } as ComplexStyleRule, + variants: { + focused: { + true: { + width: "250px", + borderColor: "#DADBE1", + }, + false: { + ":hover": { + borderColor: "rgba(255, 255, 255, 0.5)", + }, + }, + }, + }, +}); + +export const searchInput = style({ + backgroundColor: "transparent", + border: "none", + width: "100%", + height: "100%", + outline: "none", + color: "#DADBE1", + cursor: "default", + fontFamily: "inherit", + fontSize: vars.size.bodyFontSize, + textOverflow: "ellipsis", + ":focus": { + cursor: "text", + }, +}); + +export const actionButton = style({ + color: "inherit", + cursor: "pointer", + transition: "all ease 0.2s", + padding: `${SPACING_UNIT}px`, + ":hover": { + color: "#DADBE1", + }, +}); + +export const section = style({ + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT * 2}px`, + height: "100%", +}); + +export const backButton = recipe({ + base: { + color: vars.color.bodyText, + cursor: "pointer", + WebkitAppRegion: "no-drag", + position: "absolute", + transition: "transform ease 0.2s", + animationDuration: "0.2s", + width: "16px", + height: "16px", + display: "flex", + alignItems: "center", + } as ComplexStyleRule, + variants: { + enabled: { + true: { + animationName: slideIn, + }, + false: { + opacity: "0", + pointerEvents: "none", + animationName: slideOut, + }, + }, + }, +}); + +export const title = recipe({ + base: { + transition: "all ease 0.2s", + }, + variants: { + hasBackButton: { + true: { + transform: "translateX(28px)", + }, + }, + }, +}); diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx new file mode 100644 index 00000000..ea363c00 --- /dev/null +++ b/src/renderer/src/components/header/header.tsx @@ -0,0 +1,126 @@ +import { useTranslation } from "react-i18next"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { ArrowLeftIcon, SearchIcon, XIcon } from "@primer/octicons-react"; + +import { useAppDispatch, useAppSelector } from "@renderer/hooks"; + +import * as styles from "./header.css"; +import { clearSearch } from "@renderer/features"; + +export interface HeaderProps { + onSearch: (query: string) => void; + onClear: () => void; + search?: string; +} + +const pathTitle: Record = { + "/": "home", + "/catalogue": "catalogue", + "/downloads": "downloads", + "/settings": "settings", +}; + +export function Header({ onSearch, onClear, search }: HeaderProps) { + const inputRef = useRef(null); + + const navigate = useNavigate(); + const location = useLocation(); + + const { headerTitle, draggingDisabled } = useAppSelector( + (state) => state.window + ); + const dispatch = useAppDispatch(); + + const [isFocused, setIsFocused] = useState(false); + + const { t } = useTranslation("header"); + + const title = useMemo(() => { + if (location.pathname.startsWith("/game")) return headerTitle; + if (location.pathname.startsWith("/search")) return t("search_results"); + + return t(pathTitle[location.pathname]); + }, [location.pathname, headerTitle, t]); + + useEffect(() => { + if (search && !location.pathname.startsWith("/search")) { + dispatch(clearSearch()); + } + }, [location.pathname, search, dispatch]); + + const focusInput = () => { + setIsFocused(true); + inputRef.current?.focus(); + }; + + const handleBlur = () => { + setIsFocused(false); + }; + + const handleBackButtonClick = () => { + navigate(-1); + }; + + return ( +
+
+ + +

+ {title} +

+
+ +
+
+ + + onSearch(event.target.value)} + onFocus={() => setIsFocused(true)} + onBlur={handleBlur} + /> + + {search && ( + + )} +
+
+
+ ); +} diff --git a/src/renderer/src/components/hero/hero.css.ts b/src/renderer/src/components/hero/hero.css.ts new file mode 100644 index 00000000..cc4aea7a --- /dev/null +++ b/src/renderer/src/components/hero/hero.css.ts @@ -0,0 +1,64 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const hero = style({ + width: "100%", + height: "280px", + minHeight: "280px", + maxHeight: "280px", + borderRadius: "8px", + color: "#DADBE1", + overflow: "hidden", + boxShadow: "0px 0px 15px 0px #000000", + cursor: "pointer", + border: `solid 1px ${vars.color.borderColor}`, + zIndex: "1", + "@media": { + "(min-width: 1250px)": { + backgroundPosition: "center", + }, + }, +}); + +export const heroMedia = style({ + objectFit: "cover", + objectPosition: "center", + position: "absolute", + zIndex: "-1", + width: "100%", + height: "100%", + transition: "all ease 0.2s", + selectors: { + [`${hero}:hover &`]: { + transform: "scale(1.02)", + }, + }, +}); + +export const backdrop = style({ + width: "100%", + height: "100%", + background: "linear-gradient(0deg, rgba(0, 0, 0, 0.6) 25%, transparent 100%)", + position: "relative", + display: "flex", + overflow: "hidden", +}); + +export const description = style({ + maxWidth: "700px", + fontSize: vars.size.bodyFontSize, + color: "#c0c1c7", + textAlign: "left", + fontFamily: "'Fira Sans', sans-serif", + lineHeight: "20px", +}); + +export const content = style({ + width: "100%", + height: "100%", + padding: `${SPACING_UNIT * 4}px ${SPACING_UNIT * 3}px`, + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", + justifyContent: "flex-end", +}); diff --git a/src/renderer/src/components/hero/hero.tsx b/src/renderer/src/components/hero/hero.tsx new file mode 100644 index 00000000..4e766ca8 --- /dev/null +++ b/src/renderer/src/components/hero/hero.tsx @@ -0,0 +1,59 @@ +import { useNavigate } from "react-router-dom"; +import { AsyncImage } from "@renderer/components"; +import * as styles from "./hero.css"; +import { useEffect, useState } from "react"; +import { ShopDetails } from "@types"; +import { getSteamLanguage, steamUrlBuilder } from "@renderer/helpers"; +import { useTranslation } from "react-i18next"; + +const FEATURED_GAME_ID = "377160"; + +export function Hero() { + const [featuredGameDetails, setFeaturedGameDetails] = + useState(null); + + const { i18n } = useTranslation(); + + const navigate = useNavigate(); + + useEffect(() => { + window.electron + .getGameShopDetails( + FEATURED_GAME_ID, + "steam", + getSteamLanguage(i18n.language) + ) + .then((result) => { + setFeaturedGameDetails(result); + }); + }, [i18n.language]); + + return ( + + ); +} diff --git a/src/renderer/src/components/index.ts b/src/renderer/src/components/index.ts new file mode 100644 index 00000000..7e6069f1 --- /dev/null +++ b/src/renderer/src/components/index.ts @@ -0,0 +1,10 @@ +export * from "./bottom-panel/bottom-panel"; +export * from "./button/button"; +export * from "./game-card/game-card"; +export * from "./header/header"; +export * from "./hero/hero"; +export * from "./modal/modal"; +export * from "./sidebar/sidebar"; +export * from "./async-image/async-image"; +export * from "./text-field/text-field"; +export * from "./checkbox-field/checkbox-field"; diff --git a/src/renderer/src/components/modal/modal.css.ts b/src/renderer/src/components/modal/modal.css.ts new file mode 100644 index 00000000..d6e4732d --- /dev/null +++ b/src/renderer/src/components/modal/modal.css.ts @@ -0,0 +1,108 @@ +import { keyframes, style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const backdropFadeIn = keyframes({ + "0%": { backdropFilter: "blur(0px)", backgroundColor: "rgba(0, 0, 0, 0.5)" }, + "100%": { + backdropFilter: "blur(2px)", + backgroundColor: "rgba(0, 0, 0, 0.7)", + }, +}); + +export const backdropFadeOut = keyframes({ + "0%": { backdropFilter: "blur(2px)", backgroundColor: "rgba(0, 0, 0, 0.7)" }, + "100%": { + backdropFilter: "blur(0px)", + backgroundColor: "rgba(0, 0, 0, 0)", + }, +}); + +export const modalSlideIn = keyframes({ + "0%": { opacity: 0 }, + "100%": { + opacity: 1, + }, +}); + +export const modalSlideOut = keyframes({ + "0%": { opacity: 1 }, + "100%": { + opacity: 0, + }, +}); + +export const backdrop = recipe({ + base: { + animationName: backdropFadeIn, + animationDuration: "0.4s", + backgroundColor: "rgba(0, 0, 0, 0.7)", + position: "absolute", + width: "100%", + height: "100%", + display: "flex", + justifyContent: "center", + alignItems: "center", + zIndex: 1, + top: 0, + padding: `${SPACING_UNIT * 3}px`, + backdropFilter: "blur(2px)", + transition: "all ease 0.2s", + }, + variants: { + closing: { + true: { + animationName: backdropFadeOut, + backdropFilter: "blur(0px)", + backgroundColor: "rgba(0, 0, 0, 0)", + }, + }, + }, +}); + +export const modal = recipe({ + base: { + animationName: modalSlideIn, + animationDuration: "0.3s", + backgroundColor: vars.color.background, + borderRadius: "5px", + maxWidth: "600px", + color: vars.color.bodyText, + maxHeight: "100%", + border: `solid 1px ${vars.color.borderColor}`, + overflow: "hidden", + display: "flex", + flexDirection: "column", + }, + variants: { + closing: { + true: { + animationName: modalSlideOut, + opacity: 0, + }, + }, + }, +}); + +export const modalContent = style({ + height: "100%", + overflow: "auto", + padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`, +}); + +export const modalHeader = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, + padding: `${SPACING_UNIT * 2}px`, + borderBottom: `solid 1px ${vars.color.borderColor}`, + justifyContent: "space-between", + alignItems: "flex-start", +}); + +export const closeModalButton = style({ + cursor: "pointer", +}); + +export const closeModalButtonIcon = style({ + color: vars.color.bodyText, +}); diff --git a/src/renderer/src/components/modal/modal.tsx b/src/renderer/src/components/modal/modal.tsx new file mode 100644 index 00000000..35be1bf9 --- /dev/null +++ b/src/renderer/src/components/modal/modal.tsx @@ -0,0 +1,69 @@ +import { useEffect, useState } from "react"; +import { createPortal } from "react-dom"; +import { XIcon } from "@primer/octicons-react"; + +import * as styles from "./modal.css"; +import { useAppDispatch } from "@renderer/hooks"; +import { toggleDragging } from "@renderer/features"; + +export interface ModalProps { + visible: boolean; + title: string; + description: string; + onClose: () => void; + children: React.ReactNode; +} + +export function Modal({ + visible, + title, + description, + onClose, + children, +}: ModalProps) { + const [isClosing, setIsClosing] = useState(false); + const dispatch = useAppDispatch(); + + const handleCloseClick = () => { + setIsClosing(true); + const zero = performance.now(); + + requestAnimationFrame(function animateClosing(time) { + if (time - zero <= 400) { + requestAnimationFrame(animateClosing); + } else { + onClose(); + setIsClosing(false); + } + }); + }; + + useEffect(() => { + dispatch(toggleDragging(visible)); + }, [dispatch, visible]); + + if (!visible) return null; + + return createPortal( +
+
+
+
+

{title}

+

{description}

+
+ + +
+
{children}
+
+
, + document.body + ); +} diff --git a/src/renderer/src/components/sidebar/download-icon.tsx b/src/renderer/src/components/sidebar/download-icon.tsx new file mode 100644 index 00000000..474c631d --- /dev/null +++ b/src/renderer/src/components/sidebar/download-icon.tsx @@ -0,0 +1,22 @@ +import { useRef } from "react"; +import Lottie from "lottie-react"; + +import downloadingAnimation from "@renderer/assets/lottie/downloading.json"; + +export interface DownloadIconProps { + isDownloading: boolean; +} + +export function DownloadIcon({ isDownloading }: DownloadIconProps) { + const lottieRef = useRef(null); + + return ( + + ); +} diff --git a/src/renderer/src/components/sidebar/routes.tsx b/src/renderer/src/components/sidebar/routes.tsx new file mode 100644 index 00000000..8f54bac0 --- /dev/null +++ b/src/renderer/src/components/sidebar/routes.tsx @@ -0,0 +1,27 @@ +import { AppsIcon, GearIcon, HomeIcon } from "@primer/octicons-react"; +import { DownloadIcon } from "./download-icon"; + +export const routes = [ + { + path: "/", + nameKey: "home", + render: () => , + }, + { + path: "/catalogue", + nameKey: "catalogue", + render: () => , + }, + { + path: "/downloads", + nameKey: "downloads", + render: (isDownloading: boolean) => ( + + ), + }, + { + path: "/settings", + nameKey: "settings", + render: () => , + }, +]; diff --git a/src/renderer/src/components/sidebar/sidebar.css.ts b/src/renderer/src/components/sidebar/sidebar.css.ts new file mode 100644 index 00000000..e4ff22f8 --- /dev/null +++ b/src/renderer/src/components/sidebar/sidebar.css.ts @@ -0,0 +1,170 @@ +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const sidebar = recipe({ + base: { + backgroundColor: vars.color.darkBackground, + color: "#c0c1c7", + flexDirection: "column", + display: "flex", + transition: "opacity ease 0.2s", + borderRight: `solid 1px ${vars.color.borderColor}`, + position: "relative", + }, + variants: { + resizing: { + true: { + opacity: vars.opacity.active, + pointerEvents: "none", + }, + }, + }, +}); + +export const content = recipe({ + base: { + display: "flex", + flexDirection: "column", + padding: `${SPACING_UNIT * 2}px`, + paddingBottom: "0", + width: "100%", + overflow: "auto", + }, + variants: { + macos: { + true: { + paddingTop: `${SPACING_UNIT * 6}px`, + }, + }, + }, +}); + +export const handle = style({ + width: "5px", + height: "100%", + cursor: "col-resize", + position: "absolute", + right: "0", +}); + +export const menu = style({ + listStyle: "none", + padding: "0", + margin: "0", + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", + overflow: "hidden", +}); + +export const menuItem = recipe({ + base: { + transition: "all ease 0.1s", + cursor: "pointer", + textWrap: "nowrap", + display: "flex", + opacity: "0.9", + color: "#DADBE1", + ":hover": { + opacity: "1", + }, + }, + variants: { + active: { + true: { + opacity: "1", + fontWeight: "bold", + }, + }, + muted: { + true: { + opacity: vars.opacity.disabled, + ":hover": { + opacity: "1", + }, + }, + }, + }, +}); + +export const menuItemButton = style({ + color: "inherit", + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT}px`, + cursor: "pointer", + overflow: "hidden", + width: "100%", + selectors: { + [`${menuItem({ active: true }).split(" ")[1]} &`]: { + fontWeight: "bold", + }, + }, +}); + +export const menuItemButtonLabel = style({ + textOverflow: "ellipsis", + overflow: "hidden", +}); + +export const gameIcon = style({ + width: "20px", + height: "20px", + borderRadius: "4px", + backgroundSize: "cover", +}); + +export const sectionTitle = style({ + textTransform: "uppercase", + fontWeight: "bold", +}); + +export const section = recipe({ + base: { + padding: `${SPACING_UNIT * 2}px 0`, + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", + }, + variants: { + hasBorder: { + true: { + borderBottom: `solid 1px ${vars.color.borderColor}`, + }, + }, + }, +}); + +export const sidebarFooter = style({ + marginTop: "auto", + padding: `${SPACING_UNIT * 2}px`, + display: "flex", + alignItems: "center", + justifyContent: "space-between", +}); + +export const footerSocialsContainer = style({ + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT * 1.5}px`, +}); + +export const footerSocialsItem = style({ + color: vars.color.bodyText, + backgroundColor: vars.color.darkBackground, + width: "16px", + height: "16px", + display: "flex", + alignItems: "center", + transition: "all ease 0.15s", + ":hover": { + opacity: 0.75, + cursor: "pointer", + }, +}); + +export const footerText = style({ + color: vars.color.bodyText, + fontSize: "12px", +}); diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx new file mode 100644 index 00000000..5cdb2572 --- /dev/null +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -0,0 +1,255 @@ +import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useLocation, useNavigate } from "react-router-dom"; + +import type { Game } from "@types"; + +import { AsyncImage, TextField } from "@renderer/components"; +import { useDownload, useLibrary } from "@renderer/hooks"; +import { SPACING_UNIT } from "../../theme.css"; + +import { routes } from "./routes"; + +import { MarkGithubIcon } from "@primer/octicons-react"; +import DiscordLogo from "@renderer/assets/discord-icon.svg?react"; +import XLogo from "@renderer/assets/x-icon.svg?react"; + +import * as styles from "./sidebar.css"; + +const socials = [ + { + url: "https://discord.gg/hydralauncher", + icon: , + }, + { + url: "https://twitter.com/hydralauncher", + icon: , + }, + { + url: "https://github.com/hydralauncher/hydra", + icon: , + }, +]; + +const SIDEBAR_MIN_WIDTH = 200; +const SIDEBAR_INITIAL_WIDTH = 250; +const SIDEBAR_MAX_WIDTH = 450; + +const initialSidebarWidth = window.localStorage.getItem("sidebarWidth"); + +export function Sidebar() { + const { t } = useTranslation("sidebar"); + const { library, updateLibrary } = useLibrary(); + const navigate = useNavigate(); + + const [filteredLibrary, setFilteredLibrary] = useState([]); + + const [isResizing, setIsResizing] = useState(false); + const [sidebarWidth, setSidebarWidth] = useState( + initialSidebarWidth ? Number(initialSidebarWidth) : SIDEBAR_INITIAL_WIDTH + ); + + const location = useLocation(); + + const { game: gameDownloading, progress } = useDownload(); + + useEffect(() => { + updateLibrary(); + }, [gameDownloading?.id, updateLibrary]); + + const isDownloading = library.some((game) => + ["downloading", "checking_files", "downloading_metadata"].includes( + game.status + ) + ); + + const sidebarRef = useRef(null); + + const cursorPos = useRef({ x: 0 }); + const sidebarInitialWidth = useRef(0); + + const handleMouseDown: React.MouseEventHandler = ( + event + ) => { + setIsResizing(true); + cursorPos.current.x = event.screenX; + sidebarInitialWidth.current = + sidebarRef.current?.clientWidth || SIDEBAR_INITIAL_WIDTH; + }; + + const handleFilter: React.ChangeEventHandler = (event) => { + setFilteredLibrary( + library.filter((game) => + game.title + .toLowerCase() + .includes(event.target.value.toLocaleLowerCase()) + ) + ); + }; + + useEffect(() => { + setFilteredLibrary(library); + }, [library]); + + useEffect(() => { + window.onmousemove = (event) => { + if (isResizing) { + const cursorXDelta = event.screenX - cursorPos.current.x; + const newWidth = Math.max( + SIDEBAR_MIN_WIDTH, + Math.min( + sidebarInitialWidth.current + cursorXDelta, + SIDEBAR_MAX_WIDTH + ) + ); + + setSidebarWidth(newWidth); + window.localStorage.setItem("sidebarWidth", String(newWidth)); + } + }; + + window.onmouseup = () => { + if (isResizing) setIsResizing(false); + }; + + return () => { + window.onmouseup = null; + window.onmousemove = null; + }; + }, [isResizing]); + + const getGameTitle = (game: Game) => { + if (game.status === "paused") return t("paused", { title: game.title }); + + if (gameDownloading?.id === game.id) { + const isVerifying = ["downloading_metadata", "checking_files"].includes( + gameDownloading?.status + ); + + if (isVerifying) + return t(gameDownloading.status, { + title: game.title, + percentage: progress, + }); + + return t("downloading", { + title: game.title, + percentage: progress, + }); + } + + return game.title; + }; + + const handleSidebarItemClick = (path: string) => { + if (path !== location.pathname) { + navigate(path); + } + }; + + return ( + + ); +} diff --git a/src/renderer/src/components/text-field/text-field.css.ts b/src/renderer/src/components/text-field/text-field.css.ts new file mode 100644 index 00000000..d38230ef --- /dev/null +++ b/src/renderer/src/components/text-field/text-field.css.ts @@ -0,0 +1,59 @@ +import { SPACING_UNIT, vars } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; + +export const textField = recipe({ + base: { + display: "inline-flex", + transition: "all ease 0.2s", + width: "100%", + alignItems: "center", + borderRadius: "8px", + border: `solid 1px ${vars.color.borderColor}`, + height: "40px", + minHeight: "40px", + }, + variants: { + focused: { + true: { + borderColor: "#DADBE1", + }, + false: { + ":hover": { + borderColor: "rgba(255, 255, 255, 0.5)", + }, + }, + }, + theme: { + primary: { + backgroundColor: vars.color.darkBackground, + }, + dark: { + backgroundColor: vars.color.background, + }, + }, + }, +}); + +export const textFieldInput = style({ + backgroundColor: "transparent", + border: "none", + width: "100%", + height: "100%", + outline: "none", + color: "#DADBE1", + cursor: "default", + fontFamily: "inherit", + fontSize: vars.size.bodyFontSize, + textOverflow: "ellipsis", + padding: `${SPACING_UNIT}px`, + ":focus": { + cursor: "text", + }, +}); + +export const label = style({ + marginBottom: `${SPACING_UNIT}px`, + display: "block", + color: vars.color.bodyText, +}); diff --git a/src/renderer/src/components/text-field/text-field.tsx b/src/renderer/src/components/text-field/text-field.tsx new file mode 100644 index 00000000..62378615 --- /dev/null +++ b/src/renderer/src/components/text-field/text-field.tsx @@ -0,0 +1,42 @@ +import { useId, useState } from "react"; +import type { RecipeVariants } from "@vanilla-extract/recipes"; +import * as styles from "./text-field.css"; + +export interface TextFieldProps + extends React.DetailedHTMLProps< + React.InputHTMLAttributes, + HTMLInputElement + > { + theme?: RecipeVariants["theme"]; + label?: string; +} + +export function TextField({ + theme = "primary", + label, + ...props +}: TextFieldProps) { + const [isFocused, setIsFocused] = useState(false); + const id = useId(); + + return ( +
+ {label && ( + + )} + +
+ setIsFocused(true)} + onBlur={() => setIsFocused(false)} + {...props} + /> +
+
+ ); +} diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts new file mode 100644 index 00000000..6beac79f --- /dev/null +++ b/src/renderer/src/constants.ts @@ -0,0 +1 @@ +export const VERSION_CODENAME = "Exodus"; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts new file mode 100644 index 00000000..6fd533db --- /dev/null +++ b/src/renderer/src/declaration.d.ts @@ -0,0 +1,95 @@ +import type { + CatalogueCategory, + CatalogueEntry, + Game, + GameShop, + HowLongToBeatCategory, + ShopDetails, + TorrentProgress, + UserPreferences, +} from "@types"; +import type { DiskSpace } from "check-disk-space"; + +declare global { + declare module "*.svg" { + const content: React.FunctionComponent>; + export default content; + } + + interface Electron { + /* Torrenting */ + startGameDownload: ( + repackId: number, + objectID: string, + title: string, + shop: GameShop + ) => Promise; + cancelGameDownload: (gameId: number) => Promise; + pauseGameDownload: (gameId: number) => Promise; + resumeGameDownload: (gameId: number) => Promise; + onDownloadProgress: ( + cb: (value: TorrentProgress) => void + ) => () => Electron.IpcRenderer; + + /* Catalogue */ + searchGames: (query: string) => Promise; + getCatalogue: (category: CatalogueCategory) => Promise; + getGameShopDetails: ( + objectID: string, + shop: GameShop, + language: string + ) => Promise; + getRandomGame: () => Promise; + getHowLongToBeat: ( + objectID: string, + shop: GameShop, + title: string + ) => Promise; + getGames: ( + take?: number, + prevCursor?: number + ) => Promise<{ results: CatalogueEntry[]; cursor: number }>; + + /* Library */ + addGameToLibrary: ( + objectID: string, + title: string, + shop: GameShop, + executablePath: string + ) => Promise; + getLibrary: () => Promise; + getRepackersFriendlyNames: () => Promise>; + openGameInstaller: (gameId: number) => Promise; + openGame: (gameId: number, executablePath: string) => Promise; + closeGame: (gameId: number) => Promise; + removeGame: (gameId: number) => Promise; + deleteGameFolder: (gameId: number) => Promise; + getGameByObjectID: (objectID: string) => Promise; + onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer; + onGameClose: (cb: (gameId: number) => void) => () => Electron.IpcRenderer; + + /* User preferences */ + getUserPreferences: () => Promise; + updateUserPreferences: ( + preferences: Partial + ) => Promise; + + /* Hardware */ + getDiskFreeSpace: () => Promise; + + /* Misc */ + getOrCacheImage: (url: string) => Promise; + openExternal: (src: string) => Promise; + getVersion: () => Promise; + ping: () => string; + getDefaultDownloadsPath: () => Promise; + showOpenDialog: ( + options: Electron.OpenDialogOptions + ) => Promise; + platform: NodeJS.Platform; + } + + interface Window { + electron: Electron; + } +} diff --git a/src/renderer/src/features/download-slice.ts b/src/renderer/src/features/download-slice.ts new file mode 100644 index 00000000..23ba9c7e --- /dev/null +++ b/src/renderer/src/features/download-slice.ts @@ -0,0 +1,49 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; +import type { TorrentProgress } from "@types"; + +interface DownloadState { + packets: TorrentProgress[]; + gameId: number | null; + gamesWithDeletionInProgress: number[]; +} + +const initialState: DownloadState = { + packets: [], + gameId: null, + gamesWithDeletionInProgress: [], +}; + +export const downloadSlice = createSlice({ + name: "download", + initialState, + reducers: { + addPacket: (state, action: PayloadAction) => { + state.packets = [...state.packets, action.payload]; + if (!state.gameId) state.gameId = action.payload.game.id; + }, + clearDownload: (state) => { + state.packets = []; + state.gameId = null; + }, + setGameDeleting: (state, action: PayloadAction) => { + if ( + !state.gamesWithDeletionInProgress.includes(action.payload) && + action.payload + ) { + state.gamesWithDeletionInProgress.push(action.payload); + } + }, + removeGameFromDeleting: (state, action: PayloadAction) => { + const index = state.gamesWithDeletionInProgress.indexOf(action.payload); + if (index >= 0) state.gamesWithDeletionInProgress.splice(index, 1); + }, + }, +}); + +export const { + addPacket, + clearDownload, + setGameDeleting, + removeGameFromDeleting, +} = downloadSlice.actions; diff --git a/src/renderer/src/features/index.ts b/src/renderer/src/features/index.ts new file mode 100644 index 00000000..368afe0d --- /dev/null +++ b/src/renderer/src/features/index.ts @@ -0,0 +1,6 @@ +export * from "./search-slice"; +export * from "./repackers-friendly-names-slice"; +export * from "./library-slice"; +export * from "./use-preferences-slice"; +export * from "./download-slice"; +export * from "./window-slice"; diff --git a/src/renderer/src/features/library-slice.ts b/src/renderer/src/features/library-slice.ts new file mode 100644 index 00000000..15a36ba7 --- /dev/null +++ b/src/renderer/src/features/library-slice.ts @@ -0,0 +1,24 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +import type { Game } from "@types"; + +interface LibraryState { + value: Game[]; +} + +const initialState: LibraryState = { + value: [], +}; + +export const librarySlice = createSlice({ + name: "library", + initialState, + reducers: { + setLibrary: (state, action: PayloadAction) => { + state.value = action.payload; + }, + }, +}); + +export const { setLibrary } = librarySlice.actions; diff --git a/src/renderer/src/features/repackers-friendly-names-slice.ts b/src/renderer/src/features/repackers-friendly-names-slice.ts new file mode 100644 index 00000000..e7daa109 --- /dev/null +++ b/src/renderer/src/features/repackers-friendly-names-slice.ts @@ -0,0 +1,26 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +interface RepackersFriendlyNamesState { + value: Record; +} + +const initialState: RepackersFriendlyNamesState = { + value: {}, +}; + +export const repackersFriendlyNamesSlice = createSlice({ + name: "repackersFriendlyNames", + initialState, + reducers: { + setRepackersFriendlyNames: ( + state, + action: PayloadAction + ) => { + state.value = action.payload; + }, + }, +}); + +export const { setRepackersFriendlyNames } = + repackersFriendlyNamesSlice.actions; diff --git a/src/renderer/src/features/search-slice.ts b/src/renderer/src/features/search-slice.ts new file mode 100644 index 00000000..2c064aa2 --- /dev/null +++ b/src/renderer/src/features/search-slice.ts @@ -0,0 +1,25 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +interface SearchState { + value: string; +} + +const initialState: SearchState = { + value: "", +}; + +export const searchSlice = createSlice({ + name: "search", + initialState, + reducers: { + setSearch: (state, action: PayloadAction) => { + state.value = action.payload; + }, + clearSearch: (state) => { + state.value = ""; + }, + }, +}); + +export const { setSearch, clearSearch } = searchSlice.actions; diff --git a/src/renderer/src/features/use-preferences-slice.ts b/src/renderer/src/features/use-preferences-slice.ts new file mode 100644 index 00000000..f6a3cf65 --- /dev/null +++ b/src/renderer/src/features/use-preferences-slice.ts @@ -0,0 +1,23 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; +import type { UserPreferences } from "@types"; + +interface UserPreferencesState { + value: UserPreferences | null; +} + +const initialState: UserPreferencesState = { + value: null, +}; + +export const userPreferencesSlice = createSlice({ + name: "userPreferences", + initialState, + reducers: { + setUserPreferences: (state, action: PayloadAction) => { + state.value = action.payload; + }, + }, +}); + +export const { setUserPreferences } = userPreferencesSlice.actions; diff --git a/src/renderer/src/features/window-slice.ts b/src/renderer/src/features/window-slice.ts new file mode 100644 index 00000000..4d8b5fa7 --- /dev/null +++ b/src/renderer/src/features/window-slice.ts @@ -0,0 +1,27 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +interface WindowState { + draggingDisabled: boolean; + headerTitle: string; +} + +const initialState: WindowState = { + draggingDisabled: false, + headerTitle: "", +}; + +export const windowSlice = createSlice({ + name: "window", + initialState, + reducers: { + toggleDragging: (state, action: PayloadAction) => { + state.draggingDisabled = action.payload; + }, + setHeaderTitle: (state, action: PayloadAction) => { + state.headerTitle = action.payload; + }, + }, +}); + +export const { toggleDragging, setHeaderTitle } = windowSlice.actions; diff --git a/src/renderer/src/helpers.ts b/src/renderer/src/helpers.ts new file mode 100644 index 00000000..ae100f62 --- /dev/null +++ b/src/renderer/src/helpers.ts @@ -0,0 +1,25 @@ +export const steamUrlBuilder = { + library: (objectID: string) => + `https://steamcdn-a.akamaihd.net/steam/apps/${objectID}/header.jpg`, + libraryHero: (objectID: string) => + `https://steamcdn-a.akamaihd.net/steam/apps/${objectID}/library_hero.jpg`, + logo: (objectID: string) => + `https://cdn.cloudflare.steamstatic.com/steam/apps/${objectID}/logo.png`, +}; + +export const formatDownloadProgress = (progress?: number) => { + if (!progress) return "0%"; + const progressPercentage = progress * 100; + + if (Number(progressPercentage.toFixed(2)) % 1 === 0) + return `${Math.floor(progressPercentage)}%`; + + return `${progressPercentage.toFixed(2)}%`; +}; + +export const getSteamLanguage = (language: string) => { + if (language.startsWith("pt")) return "brazilian"; + if (language.startsWith("es")) return "spanish"; + if (language.startsWith("fr")) return "french"; + return "english"; +}; diff --git a/src/renderer/src/hooks/index.ts b/src/renderer/src/hooks/index.ts new file mode 100644 index 00000000..aae58295 --- /dev/null +++ b/src/renderer/src/hooks/index.ts @@ -0,0 +1,3 @@ +export * from "./use-download"; +export * from "./use-library"; +export * from "./redux"; diff --git a/src/renderer/src/hooks/redux.ts b/src/renderer/src/hooks/redux.ts new file mode 100644 index 00000000..ebfd6382 --- /dev/null +++ b/src/renderer/src/hooks/redux.ts @@ -0,0 +1,7 @@ +import { useDispatch, useSelector } from "react-redux"; +import type { TypedUseSelectorHook } from "react-redux"; + +import type { AppDispatch, RootState } from "@renderer/store"; + +export const useAppDispatch: () => AppDispatch = useDispatch; +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/src/renderer/src/hooks/use-date.ts b/src/renderer/src/hooks/use-date.ts new file mode 100644 index 00000000..2ffcb583 --- /dev/null +++ b/src/renderer/src/hooks/use-date.ts @@ -0,0 +1,33 @@ +import { formatDistance } from "date-fns"; +import type { FormatDistanceOptions } from "date-fns"; +import { ptBR, enUS, es, fr } from "date-fns/locale"; +import { useTranslation } from "react-i18next"; + +export function useDate() { + const { i18n } = useTranslation(); + + const getDateLocale = () => { + if (i18n.language.startsWith("pt")) return ptBR; + if (i18n.language.startsWith("es")) return es; + if (i18n.language.startsWith("fr")) return fr; + + return enUS; + }; + + return { + formatDistance: ( + date: string | number | Date, + baseDate: string | number | Date, + options?: FormatDistanceOptions + ) => { + try { + return formatDistance(date, baseDate, { + ...options, + locale: getDateLocale(), + }); + } catch (err) { + return ""; + } + }, + }; +} diff --git a/src/renderer/src/hooks/use-download.ts b/src/renderer/src/hooks/use-download.ts new file mode 100644 index 00000000..522dd2c2 --- /dev/null +++ b/src/renderer/src/hooks/use-download.ts @@ -0,0 +1,132 @@ +import { addMilliseconds } from "date-fns"; + +import { formatDownloadProgress } from "@renderer/helpers"; +import { useLibrary } from "./use-library"; +import { useAppDispatch, useAppSelector } from "./redux"; +import { + addPacket, + clearDownload, + setGameDeleting, + removeGameFromDeleting, +} from "@renderer/features"; +import type { GameShop, TorrentProgress } from "@types"; +import { useDate } from "./use-date"; +import { formatBytes } from "@renderer/utils"; + +export function useDownload() { + const { updateLibrary } = useLibrary(); + const { formatDistance } = useDate(); + + const { packets, gamesWithDeletionInProgress } = useAppSelector( + (state) => state.download + ); + const dispatch = useAppDispatch(); + + const lastPacket = packets.at(-1); + + const startDownload = ( + repackId: number, + objectID: string, + title: string, + shop: GameShop + ) => + window.electron + .startGameDownload(repackId, objectID, title, shop) + .then((game) => { + dispatch(clearDownload()); + updateLibrary(); + + return game; + }); + + const pauseDownload = (gameId: number) => + window.electron.pauseGameDownload(gameId).then(() => { + dispatch(clearDownload()); + updateLibrary(); + }); + + const resumeDownload = (gameId: number) => + window.electron.resumeGameDownload(gameId).then(() => { + updateLibrary(); + }); + + const cancelDownload = (gameId: number) => + window.electron.cancelGameDownload(gameId).then(() => { + dispatch(clearDownload()); + updateLibrary(); + deleteGame(gameId); + }); + + const removeGame = (gameId: number) => + window.electron.removeGame(gameId).then(() => { + updateLibrary(); + }); + + const isVerifying = ["downloading_metadata", "checking_files"].includes( + lastPacket?.game.status + ); + + const getETA = () => { + if (isVerifying || !isFinite(lastPacket?.timeRemaining)) { + return ""; + } + + try { + return formatDistance( + addMilliseconds(new Date(), lastPacket?.timeRemaining ?? 1), + new Date(), + { addSuffix: true } + ); + } catch (err) { + return ""; + } + }; + + const getProgress = () => { + if (lastPacket?.game.status === "checking_files") { + return formatDownloadProgress(lastPacket?.game.fileVerificationProgress); + } + + return formatDownloadProgress(lastPacket?.game.progress); + }; + + const deleteGame = (gameId: number) => + window.electron + .cancelGameDownload(gameId) + .then(() => { + dispatch(setGameDeleting(gameId)); + return window.electron.deleteGameFolder(gameId); + }) + .catch(() => {}) + .finally(() => { + updateLibrary(); + dispatch(removeGameFromDeleting(gameId)); + }); + + const isGameDeleting = (gameId: number) => { + return gamesWithDeletionInProgress.includes(gameId); + }; + + return { + game: lastPacket?.game, + bytesDownloaded: lastPacket?.game.bytesDownloaded, + fileSize: lastPacket?.game.fileSize, + isVerifying, + gameId: lastPacket?.game.id, + downloadSpeed: `${formatBytes(lastPacket?.downloadSpeed ?? 0)}/s`, + isDownloading: Boolean(lastPacket), + progress: getProgress(), + numPeers: lastPacket?.numPeers, + numSeeds: lastPacket?.numSeeds, + eta: getETA(), + startDownload, + pauseDownload, + resumeDownload, + cancelDownload, + removeGame, + deleteGame, + isGameDeleting, + clearDownload: () => dispatch(clearDownload()), + addPacket: (packet: TorrentProgress) => dispatch(addPacket(packet)), + }; +} diff --git a/src/renderer/src/hooks/use-library.ts b/src/renderer/src/hooks/use-library.ts new file mode 100644 index 00000000..f7310df0 --- /dev/null +++ b/src/renderer/src/hooks/use-library.ts @@ -0,0 +1,16 @@ +import { useCallback } from "react"; +import { useAppDispatch, useAppSelector } from "./redux"; +import { setLibrary } from "@renderer/features"; + +export function useLibrary() { + const dispatch = useAppDispatch(); + const library = useAppSelector((state) => state.library.value); + + const updateLibrary = useCallback(async () => { + return window.electron + .getLibrary() + .then((updatedLibrary) => dispatch(setLibrary(updatedLibrary))); + }, [dispatch]); + + return { library, updateLibrary }; +} diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx new file mode 100644 index 00000000..2199cc0a --- /dev/null +++ b/src/renderer/src/main.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import { Provider } from "react-redux"; +import LanguageDetector from "i18next-browser-languagedetector"; +import { createHashRouter, RouterProvider } from "react-router-dom"; + +import { init } from "@sentry/electron/renderer"; +import { init as reactInit } from "@sentry/react"; + +import "@fontsource/fira-mono/400.css"; +import "@fontsource/fira-mono/500.css"; +import "@fontsource/fira-mono/700.css"; +import "@fontsource/fira-sans/400.css"; +import "@fontsource/fira-sans/500.css"; +import "@fontsource/fira-sans/700.css"; +import "react-loading-skeleton/dist/skeleton.css"; + +import { App } from "./app"; +import { + Home, + Downloads, + GameDetails, + SearchResults, + Settings, + Catalogue, +} from "@renderer/pages"; + +import { store } from "./store"; + +import * as resources from "@locales"; + +if (import.meta.env.RENDERER_VITE_SENTRY_DSN) { + init( + { + dsn: import.meta.env.RENDERER_VITE_SENTRY_DSN, + beforeSend: async (event) => { + const userPreferences = await window.electron.getUserPreferences(); + + if (userPreferences?.telemetryEnabled) return event; + return null; + }, + }, + reactInit + ); +} + +const router = createHashRouter([ + { + path: "/", + Component: App, + children: [ + { + path: "/", + Component: Home, + }, + { + path: "/catalogue", + Component: Catalogue, + }, + { + path: "/downloads", + Component: Downloads, + }, + { + path: "/game/:shop/:objectID", + Component: GameDetails, + }, + { + path: "/search", + Component: SearchResults, + }, + { + path: "/settings", + Component: Settings, + }, + ], + }, +]); + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, + }) + .then(() => { + window.electron.updateUserPreferences({ language: i18n.language }); + }); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + +); diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx new file mode 100644 index 00000000..8dc5fc56 --- /dev/null +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -0,0 +1,115 @@ +import { Button, GameCard } from "@renderer/components"; +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import { useTranslation } from "react-i18next"; + +import type { CatalogueEntry } from "@types"; + +import { clearSearch } from "@renderer/features"; +import { useAppDispatch } from "@renderer/hooks"; +import { vars } from "../../theme.css"; +import { useEffect, useRef, useState } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import * as styles from "../home/home.css"; +import { ArrowLeftIcon, ArrowRightIcon } from "@primer/octicons-react"; + +export function Catalogue() { + const dispatch = useAppDispatch(); + + const { t } = useTranslation("catalogue"); + + const [searchResults, setSearchResults] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const contentRef = useRef(null); + + const cursorRef = useRef(0); + + const navigate = useNavigate(); + + const [searchParams] = useSearchParams(); + const cursor = Number(searchParams.get("cursor") ?? 0); + + const handleGameClick = (game: CatalogueEntry) => { + dispatch(clearSearch()); + navigate(`/game/${game.shop}/${game.objectID}`); + }; + + useEffect(() => { + if (contentRef.current) contentRef.current.scrollTop = 0; + setIsLoading(true); + setSearchResults([]); + + window.electron + .getGames(24, cursor) + .then(({ results, cursor }) => { + return new Promise((resolve) => { + setTimeout(() => { + cursorRef.current = cursor; + setSearchResults(results); + resolve(null); + }, 500); + }); + }) + .finally(() => { + setIsLoading(false); + }); + }, [dispatch, cursor, searchParams]); + + const handleNextPage = () => { + const params = new URLSearchParams({ + cursor: cursorRef.current.toString(), + }); + + navigate(`/catalogue?${params.toString()}`); + }; + + return ( + +
+ + + +
+ +
+
+ {isLoading && + Array.from({ length: 12 }).map((_, index) => ( + + ))} + + {!isLoading && searchResults.length > 0 && ( + <> + {searchResults.map((game) => ( + handleGameClick(game)} + /> + ))} + + )} +
+
+
+ ); +} diff --git a/src/renderer/src/pages/downloads/delete-modal.css.ts b/src/renderer/src/pages/downloads/delete-modal.css.ts new file mode 100644 index 00000000..ef0ba179 --- /dev/null +++ b/src/renderer/src/pages/downloads/delete-modal.css.ts @@ -0,0 +1,10 @@ +import { SPACING_UNIT } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; + +export const deleteActionsButtonsCtn = style({ + display: "flex", + width: "100%", + justifyContent: "end", + alignItems: "center", + gap: `${SPACING_UNIT}px`, +}); diff --git a/src/renderer/src/pages/downloads/delete-modal.tsx b/src/renderer/src/pages/downloads/delete-modal.tsx new file mode 100644 index 00000000..953d94cd --- /dev/null +++ b/src/renderer/src/pages/downloads/delete-modal.tsx @@ -0,0 +1,43 @@ +import { useTranslation } from "react-i18next"; + +import { Button, Modal } from "@renderer/components"; + +import * as styles from "./delete-modal.css"; + +interface DeleteModalProps { + visible: boolean; + onClose: () => void; + deleteGame: () => void; +} + +export function DeleteModal({ + onClose, + visible, + deleteGame, +}: DeleteModalProps) { + const { t } = useTranslation("downloads"); + + const handleDeleteGame = () => { + deleteGame(); + onClose(); + }; + + return ( + +
+ + + +
+
+ ); +} diff --git a/src/renderer/src/pages/downloads/downloads.css.ts b/src/renderer/src/pages/downloads/downloads.css.ts new file mode 100644 index 00000000..fd68de81 --- /dev/null +++ b/src/renderer/src/pages/downloads/downloads.css.ts @@ -0,0 +1,90 @@ +import { SPACING_UNIT, vars } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; + +export const downloadTitle = style({ + fontWeight: "bold", + cursor: "pointer", + color: vars.color.bodyText, + textAlign: "left", + marginBottom: `${SPACING_UNIT}px`, + fontSize: "16px", + display: "block", + ":hover": { + textDecoration: "underline", + }, +}); + +export const downloads = style({ + width: "100%", + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", + margin: "0", + padding: "0", + marginTop: `${SPACING_UNIT * 3}px`, +}); + +export const downloadCover = style({ + width: "280px", + minWidth: "280px", + height: "auto", + objectFit: "cover", + objectPosition: "center", + borderRight: `solid 1px ${vars.color.borderColor}`, +}); + +export const download = recipe({ + base: { + width: "100%", + backgroundColor: vars.color.background, + display: "flex", + borderRadius: "8px", + border: `solid 1px ${vars.color.borderColor}`, + overflow: "hidden", + boxShadow: "0px 0px 15px 0px #000000", + transition: "all ease 0.2s", + height: "140px", + minHeight: "140px", + maxHeight: "140px", + }, + variants: { + cancelled: { + true: { + opacity: vars.opacity.disabled, + ":hover": { + opacity: "1", + }, + }, + }, + }, +}); + +export const downloadDetails = style({ + display: "flex", + flexDirection: "column", + flex: "1", + justifyContent: "center", + gap: `${SPACING_UNIT / 2}px`, + fontSize: "14px", +}); + +export const downloadRightContent = style({ + display: "flex", + padding: `${SPACING_UNIT * 2}px`, + flex: "1", + gap: `${SPACING_UNIT}px`, +}); + +export const downloadActions = style({ + display: "flex", + alignItems: "center", + gap: `${SPACING_UNIT}px`, +}); + +export const downloadsContainer = style({ + display: "flex", + padding: `${SPACING_UNIT * 3}px`, + flexDirection: "column", + width: "100%", +}); diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx new file mode 100644 index 00000000..aecf7b5e --- /dev/null +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -0,0 +1,272 @@ +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; + +import { AsyncImage, Button, TextField } from "@renderer/components"; +import { formatDownloadProgress, steamUrlBuilder } from "@renderer/helpers"; +import { useDownload, useLibrary } from "@renderer/hooks"; +import type { Game } from "@types"; + +import { useEffect, useMemo, useRef, useState } from "react"; +import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; +import * as styles from "./downloads.css"; +import { DeleteModal } from "./delete-modal"; +import { formatBytes } from "@renderer/utils"; + +export function Downloads() { + const { library, updateLibrary } = useLibrary(); + + const { t } = useTranslation("downloads"); + + const navigate = useNavigate(); + + const gameToBeDeleted = useRef(null); + + const [filteredLibrary, setFilteredLibrary] = useState([]); + const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false); + const [showDeleteModal, setShowDeleteModal] = useState(false); + + const { + game: gameDownloading, + progress, + isDownloading, + numPeers, + numSeeds, + pauseDownload, + resumeDownload, + cancelDownload, + deleteGame, + isGameDeleting, + } = useDownload(); + + const libraryWithDownloadedGamesOnly = useMemo(() => { + return library.filter((game) => game.status); + }, [library]); + + useEffect(() => { + setFilteredLibrary(libraryWithDownloadedGamesOnly); + }, [libraryWithDownloadedGamesOnly]); + + const openGameInstaller = (gameId: number) => + window.electron.openGameInstaller(gameId).then((isBinaryInPath) => { + if (!isBinaryInPath) setShowBinaryNotFoundModal(true); + updateLibrary(); + }); + + const removeGame = (gameId: number) => + window.electron.removeGame(gameId).then(() => { + updateLibrary(); + }); + + const getFinalDownloadSize = (game: Game) => { + const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; + + if (!game) return "N/A"; + if (game.fileSize) return formatBytes(game.fileSize); + + if (gameDownloading?.fileSize && isGameDownloading) + return formatBytes(gameDownloading.fileSize); + + return game.repack?.fileSize ?? "N/A"; + }; + + const getGameInfo = (game: Game) => { + const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; + const finalDownloadSize = getFinalDownloadSize(game); + + if (isGameDeleting(game?.id)) { + return

{t("deleting")}

; + } + + if (isGameDownloading) { + return ( + <> +

{progress}

+ + {gameDownloading?.status !== "downloading" ? ( +

{t(gameDownloading?.status)}

+ ) : ( + <> +

+ {formatBytes(gameDownloading?.bytesDownloaded)} /{" "} + {finalDownloadSize} +

+

+ {numPeers} peers / {numSeeds} seeds +

+ + )} + + ); + } + + if (game?.status === "seeding") { + return ( + <> +

{game?.repack.title}

+

{t("completed")}

+ + ); + } + if (game?.status === "cancelled") return

{t("cancelled")}

; + if (game?.status === "downloading_metadata") + return

{t("starting_download")}

; + + if (game?.status === "paused") { + return ( + <> +

{formatDownloadProgress(game.progress)}

+

{t("paused")}

+ + ); + } + }; + + const openDeleteModal = (gameId: number) => { + gameToBeDeleted.current = gameId; + setShowDeleteModal(true); + }; + + const getGameActions = (game: Game) => { + const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; + + const deleting = isGameDeleting(game.id); + + if (isGameDownloading) { + return ( + <> + + + + ); + } + + if (game?.status === "paused") { + return ( + <> + + + + ); + } + + if (game?.status === "seeding") { + return ( + <> + + + + + ); + } + + if (game?.status === "downloading_metadata") { + return ( + + ); + } + + return ( + <> + + + + + ); + }; + + const handleFilter: React.ChangeEventHandler = (event) => { + setFilteredLibrary( + libraryWithDownloadedGamesOnly.filter((game) => + game.title + .toLowerCase() + .includes(event.target.value.toLocaleLowerCase()) + ) + ); + }; + + return ( +
+ setShowBinaryNotFoundModal(false)} + /> + setShowDeleteModal(false)} + deleteGame={() => + deleteGame(gameToBeDeleted.current).then(updateLibrary) + } + /> + + + +
    + {filteredLibrary.map((game) => { + return ( +
  • + +
    +
    + + + {getGameInfo(game)} +
    + +
    + {getGameActions(game)} +
    +
    +
  • + ); + })} +
+
+ ); +} diff --git a/src/renderer/src/pages/game-details/description-header.tsx b/src/renderer/src/pages/game-details/description-header.tsx new file mode 100644 index 00000000..e502a4fb --- /dev/null +++ b/src/renderer/src/pages/game-details/description-header.tsx @@ -0,0 +1,84 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useParams } from "react-router-dom"; +import { ShareAndroidIcon } from "@primer/octicons-react"; + +import { Button } from "@renderer/components"; +import type { ShopDetails } from "@types"; + +import * as styles from "./game-details.css"; + +const OPEN_HYDRA_URL = "https://open.hydralauncher.site"; + +export interface DescriptionHeaderProps { + gameDetails: ShopDetails | null; +} + +export function DescriptionHeader({ gameDetails }: DescriptionHeaderProps) { + const [clipboardLock, setClipboardLock] = useState(false); + const { t, i18n } = useTranslation("game_details"); + + const { objectID, shop } = useParams(); + + useEffect(() => { + if (!gameDetails) return setClipboardLock(true); + setClipboardLock(false); + }, [gameDetails]); + + const handleCopyToClipboard = () => { + setClipboardLock(true); + + const searchParams = new URLSearchParams({ + p: btoa( + JSON.stringify([ + objectID, + shop, + encodeURIComponent(gameDetails?.name), + i18n.language, + ]) + ), + }); + + navigator.clipboard.writeText( + OPEN_HYDRA_URL + `/?${searchParams.toString()}` + ); + + const zero = performance.now(); + + requestAnimationFrame(function holdLock(time) { + if (time - zero <= 3000) { + requestAnimationFrame(holdLock); + } else { + setClipboardLock(false); + } + }); + }; + + return ( +
+
+

+ {t("release_date", { + date: gameDetails?.release_date.date, + })} +

+

{t("publisher", { publisher: gameDetails?.publishers[0] })}

+
+ + +
+ ); +} diff --git a/src/renderer/src/pages/game-details/game-details-skeleton.tsx b/src/renderer/src/pages/game-details/game-details-skeleton.tsx new file mode 100644 index 00000000..4a202d18 --- /dev/null +++ b/src/renderer/src/pages/game-details/game-details-skeleton.tsx @@ -0,0 +1,89 @@ +import Skeleton from "react-loading-skeleton"; + +import { Button } from "@renderer/components"; +import * as styles from "./game-details.css"; +import { useTranslation } from "react-i18next"; +import { ShareAndroidIcon } from "@primer/octicons-react"; + +export function GameDetailsSkeleton() { + const { t } = useTranslation("game_details"); + + return ( +
+
+ +
+
+
+ + +
+
+
+
+
+
+ + +
+ +
+
+ {Array.from({ length: 3 }).map((_, index) => ( + + ))} + + {Array.from({ length: 2 }).map((_, index) => ( + + ))} + + +
+
+
+
+

HowLongToBeat

+
+
    + {Array.from({ length: 3 }).map((_, index) => ( + + ))} +
+
+

{t("requirements")}

+
+
+ + +
+
+ {Array.from({ length: 6 }).map((_, index) => ( + + ))} +
+
+
+
+ ); +} diff --git a/src/renderer/src/pages/game-details/game-details.css.ts b/src/renderer/src/pages/game-details/game-details.css.ts new file mode 100644 index 00000000..52f671eb --- /dev/null +++ b/src/renderer/src/pages/game-details/game-details.css.ts @@ -0,0 +1,259 @@ +import { globalStyle, keyframes, style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const slideIn = keyframes({ + "0%": { transform: `translateY(${40 + 16}px)` }, + "100%": { transform: "translateY(0)" }, +}); + +export const hero = style({ + width: "100%", + height: "300px", + minHeight: "300px", + display: "flex", + flexDirection: "column", + position: "relative", + transition: "all ease 0.2s", + "@media": { + "(min-width: 1250px)": { + height: "350px", + minHeight: "350px", + }, + }, +}); + +export const heroContent = style({ + padding: `${SPACING_UNIT * 2}px`, + height: "100%", + width: "100%", + display: "flex", +}); + +export const heroBackdrop = style({ + width: "100%", + height: "100%", + background: "linear-gradient(0deg, rgba(0, 0, 0, 0.3) 60%, transparent 100%)", + position: "absolute", + display: "flex", + flexDirection: "column", + justifyContent: "space-between", +}); + +export const heroImage = style({ + width: "100%", + height: "100%", + objectFit: "cover", + objectPosition: "top", + transition: "all ease 0.2s", + "@media": { + "(min-width: 1250px)": { + objectPosition: "center", + }, + }, +}); + +export const heroImageSkeleton = style({ + height: "300px", + "@media": { + "(min-width: 1250px)": { + height: "350px", + }, + }, +}); + +export const container = style({ + width: "100%", + height: "100%", + display: "flex", + flexDirection: "column", +}); + +export const descriptionContainer = style({ + display: "flex", + width: "100%", + flex: "1", +}); + +export const descriptionContent = style({ + width: "100%", + height: "100%", +}); + +export const contentSidebar = style({ + borderLeft: `solid 1px ${vars.color.borderColor};`, + width: "100%", + height: "100%", + "@media": { + "(min-width: 768px)": { + width: "100%", + maxWidth: "200px", + }, + "(min-width: 1024px)": { + maxWidth: "300px", + width: "100%", + }, + "(min-width: 1280px)": { + width: "100%", + maxWidth: "400px", + }, + }, +}); + +export const contentSidebarTitle = style({ + height: "72px", + padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`, + display: "flex", + alignItems: "center", + backgroundColor: vars.color.background, + borderBottom: `solid 1px ${vars.color.borderColor}`, +}); + +export const requirementButtonContainer = style({ + width: "100%", + display: "flex", +}); + +export const requirementButton = style({ + border: `solid 1px ${vars.color.borderColor};`, + borderLeft: "none", + borderRight: "none", + borderRadius: "0", + width: "100%", +}); + +export const requirementsDetails = style({ + padding: `${SPACING_UNIT * 2}px`, + lineHeight: "22px", + fontFamily: "'Fira Sans', sans-serif", + fontSize: "16px", +}); + +export const requirementsDetailsSkeleton = style({ + display: "flex", + flexDirection: "column", + gap: "8px", + padding: `${SPACING_UNIT * 2}px`, + fontSize: "16px", +}); + +export const description = style({ + userSelect: "text", + lineHeight: "22px", + fontFamily: "'Fira Sans', sans-serif", + fontSize: "16px", + padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`, + "@media": { + "(min-width: 1280px)": { + width: "60%", + }, + }, + width: "100%", + marginLeft: "auto", + marginRight: "auto", +}); + +export const descriptionSkeleton = style({ + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT}px`, + padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`, + width: "100%", + "@media": { + "(min-width: 1280px)": { + width: "60%", + lineHeight: "22px", + }, + }, + marginLeft: "auto", + marginRight: "auto", +}); + +export const descriptionHeader = style({ + width: "100%", + padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`, + borderBottom: `solid 1px ${vars.color.borderColor}`, + display: "flex", + justifyContent: "space-between", + alignItems: "center", + backgroundColor: vars.color.background, + height: "72px", +}); + +export const descriptionHeaderInfo = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, + flexDirection: "column", + fontSize: vars.size.bodyFontSize, +}); + +export const howLongToBeatCategoriesList = style({ + margin: "0", + padding: "16px", + display: "flex", + flexDirection: "column", + gap: "16px", +}); + +export const howLongToBeatCategory = style({ + display: "flex", + flexDirection: "column", + gap: "4px", + backgroundColor: vars.color.background, + borderRadius: "8px", + padding: `8px 16px`, + border: `solid 1px ${vars.color.borderColor}`, +}); + +export const howLongToBeatCategoryLabel = style({ + fontSize: vars.size.bodyFontSize, + color: "#DADBE1", +}); + +export const howLongToBeatCategorySkeleton = style({ + border: `solid 1px ${vars.color.borderColor}`, + borderRadius: "8px", + height: "76px", +}); + +export const randomizerButton = style({ + animationName: slideIn, + animationDuration: "0.2s", + position: "fixed", + /* Bottom panel height + spacing */ + bottom: `${26 + SPACING_UNIT * 2}px`, + /* Scroll bar + spacing */ + right: `${9 + SPACING_UNIT * 2}px`, + boxShadow: "rgba(255, 255, 255, 0.1) 0px 0px 10px 3px", + border: `solid 2px ${vars.color.borderColor}`, + backgroundColor: vars.color.background, + ":hover": { + backgroundColor: vars.color.background, + boxShadow: "rgba(255, 255, 255, 0.1) 0px 0px 15px 5px", + opacity: "1", + }, + ":active": { + transform: "scale(0.98)", + }, +}); + +globalStyle(".bb_tag", { + marginTop: `${SPACING_UNIT * 2}px`, + marginBottom: `${SPACING_UNIT * 2}px`, +}); + +globalStyle(`${description} img`, { + borderRadius: "5px", + marginTop: `${SPACING_UNIT}px`, + marginBottom: `${SPACING_UNIT * 3}px`, + display: "block", + maxWidth: "100%", +}); + +globalStyle(`${description} a`, { + color: vars.color.bodyText, +}); + +globalStyle(`${requirementsDetails} a`, { + display: "flex", + color: vars.color.bodyText, +}); diff --git a/src/renderer/src/pages/game-details/game-details.tsx b/src/renderer/src/pages/game-details/game-details.tsx new file mode 100644 index 00000000..7f9e8ac2 --- /dev/null +++ b/src/renderer/src/pages/game-details/game-details.tsx @@ -0,0 +1,289 @@ +import Color from "color"; +import { average } from "color.js"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; + +import type { + Game, + GameShop, + HowLongToBeatCategory, + ShopDetails, + SteamAppDetails, +} from "@types"; + +import { AsyncImage, Button } from "@renderer/components"; +import { setHeaderTitle } from "@renderer/features"; +import { getSteamLanguage, steamUrlBuilder } from "@renderer/helpers"; +import { useAppDispatch, useDownload } from "@renderer/hooks"; + +import starsAnimation from "@renderer/assets/lottie/stars.json"; + +import { vars } from "../../theme.css"; +import { useTranslation } from "react-i18next"; +import { SkeletonTheme } from "react-loading-skeleton"; +import { GameDetailsSkeleton } from "./game-details-skeleton"; +import * as styles from "./game-details.css"; +import { HeroPanel } from "./hero-panel"; +import { HowLongToBeatSection } from "./how-long-to-beat-section"; +import { RepacksModal } from "./repacks-modal"; +import Lottie from "lottie-react"; +import { DescriptionHeader } from "./description-header"; + +export function GameDetails() { + const { objectID, shop } = useParams(); + + const [isLoading, setIsLoading] = useState(false); + const [color, setColor] = useState(""); + const [gameDetails, setGameDetails] = useState(null); + const [howLongToBeat, setHowLongToBeat] = useState<{ + isLoading: boolean; + data: HowLongToBeatCategory[] | null; + }>({ isLoading: true, data: null }); + + const [game, setGame] = useState(null); + const [isGamePlaying, setIsGamePlaying] = useState(false); + const [activeRequirement, setActiveRequirement] = + useState("minimum"); + + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + const { t, i18n } = useTranslation("game_details"); + + const [showRepacksModal, setShowRepacksModal] = useState(false); + + const randomGameObjectID = useRef(null); + + const dispatch = useAppDispatch(); + + const { game: gameDownloading, startDownload, isDownloading } = useDownload(); + + const getRandomGame = useCallback(() => { + window.electron.getRandomGame().then((objectID) => { + randomGameObjectID.current = objectID; + }); + }, []); + + const handleImageSettled = useCallback((url: string) => { + average(url, { amount: 1, format: "hex" }) + .then((color) => { + setColor(new Color(color).darken(0.6).toString() as string); + }) + .catch(() => {}); + }, []); + + const getGame = useCallback(() => { + window.electron + .getGameByObjectID(objectID) + .then((result) => setGame(result)); + }, [setGame, objectID]); + + useEffect(() => { + getGame(); + }, [getGame, gameDownloading?.id]); + + useEffect(() => { + setGame(null); + setIsLoading(true); + setIsGamePlaying(false); + dispatch(setHeaderTitle("")); + + getRandomGame(); + + window.electron + .getGameShopDetails(objectID, "steam", getSteamLanguage(i18n.language)) + .then((result) => { + if (!result) { + navigate(-1); + return; + } + + window.electron + .getHowLongToBeat(objectID, "steam", result.name) + .then((data) => { + setHowLongToBeat({ isLoading: false, data }); + }); + + setGameDetails(result); + dispatch(setHeaderTitle(result.name)); + }) + .finally(() => { + setIsLoading(false); + }); + + getGame(); + setHowLongToBeat({ isLoading: true, data: null }); + }, [getGame, getRandomGame, dispatch, navigate, objectID, i18n.language]); + + const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; + + useEffect(() => { + if (isGameDownloading) + setGame((prev) => ({ ...prev, status: gameDownloading?.status })); + }, [isGameDownloading, gameDownloading?.status]); + + useEffect(() => { + const listeners = [ + window.electron.onGameClose(() => { + if (isGamePlaying) setIsGamePlaying(false); + }), + window.electron.onPlaytime((gameId) => { + if (gameId === game?.id) { + if (!isGamePlaying) setIsGamePlaying(true); + getGame(); + } + }), + ]; + + return () => { + listeners.forEach((unsubscribe) => unsubscribe()); + }; + }, [game?.id, isGamePlaying, getGame]); + + const handleStartDownload = async (repackId: number) => { + return startDownload( + repackId, + gameDetails.objectID, + gameDetails.name, + shop as GameShop + ).then(() => { + getGame(); + setShowRepacksModal(false); + }); + }; + + const handleRandomizerClick = () => { + if (!randomGameObjectID.current) return; + + const searchParams = new URLSearchParams({ + fromRandomizer: "1", + }); + + navigate( + `/game/steam/${randomGameObjectID.current}?${searchParams.toString()}` + ); + }; + + const fromRandomizer = searchParams.get("fromRandomizer"); + + return ( + + {gameDetails && ( + setShowRepacksModal(false)} + /> + )} + + {isLoading ? ( + + ) : ( +
+
+ +
+
+ +
+
+
+ + setShowRepacksModal(true)} + getGame={getGame} + isGamePlaying={isGamePlaying} + /> + +
+
+ + +
+
+ +
+ + +
+

{t("requirements")}

+
+ +
+ + +
+ +
+
+
+
+ )} + + {fromRandomizer && ( + + )} +
+ ); +} diff --git a/src/renderer/src/pages/game-details/hero-panel-actions.tsx b/src/renderer/src/pages/game-details/hero-panel-actions.tsx new file mode 100644 index 00000000..17394e7e --- /dev/null +++ b/src/renderer/src/pages/game-details/hero-panel-actions.tsx @@ -0,0 +1,212 @@ +import { NoEntryIcon, PlusCircleIcon } from "@primer/octicons-react"; + +import { Button } from "@renderer/components"; +import { useDownload, useLibrary } from "@renderer/hooks"; +import type { Game, ShopDetails } from "@types"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; + +export interface HeroPanelActionsProps { + game: Game | null; + gameDetails: ShopDetails | null; + isGamePlaying: boolean; + isGameDownloading: boolean; + openRepacksModal: () => void; + openBinaryNotFoundModal: () => void; + getGame: () => void; +} + +export function HeroPanelActions({ + game, + gameDetails, + isGamePlaying, + isGameDownloading, + openRepacksModal, + openBinaryNotFoundModal, + getGame, +}: HeroPanelActionsProps) { + const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] = + useState(false); + + const { + resumeDownload, + pauseDownload, + cancelDownload, + removeGame, + isGameDeleting, + } = useDownload(); + + const { updateLibrary } = useLibrary(); + + const { t } = useTranslation("game_details"); + + const selectGameExecutable = async () => { + return window.electron + .showOpenDialog({ + properties: ["openFile"], + filters: [ + { + name: "Game executable", + extensions: window.electron.platform === "win32" ? ["exe"] : [], + }, + ], + }) + .then(({ filePaths }) => { + if (filePaths && filePaths.length > 0) { + return filePaths[0]; + } + }); + }; + + const toggleGameOnLibrary = async () => { + setToggleLibraryGameDisabled(true); + + try { + if (game) { + await removeGame(game.id); + } else { + const gameExecutablePath = await selectGameExecutable(); + + await window.electron.addGameToLibrary( + gameDetails.objectID, + gameDetails.name, + "steam", + gameExecutablePath + ); + } + + updateLibrary(); + getGame(); + } finally { + setToggleLibraryGameDisabled(false); + } + }; + + const openGameInstaller = () => { + window.electron.openGameInstaller(game.id).then((isBinaryInPath) => { + if (!isBinaryInPath) openBinaryNotFoundModal(); + updateLibrary(); + }); + }; + + const openGame = async () => { + if (game.executablePath) { + window.electron.openGame(game.id, game.executablePath); + return; + } + + if (game?.executablePath) { + window.electron.openGame(game.id, game.executablePath); + return; + } + + const gameExecutablePath = await selectGameExecutable(); + window.electron.openGame(game.id, gameExecutablePath); + }; + + const closeGame = () => window.electron.closeGame(game.id); + + const deleting = isGameDeleting(game?.id); + + const toggleGameOnLibraryButton = ( + + ); + + if (isGameDownloading) { + return ( + <> + + + + ); + } + + if (game?.status === "paused") { + return ( + <> + + + + ); + } + + if (game?.status === "seeding" || (game && !game.status)) { + return ( + <> + {game?.status === "seeding" ? ( + + ) : ( + toggleGameOnLibraryButton + )} + + {isGamePlaying ? ( + + ) : ( + + )} + + ); + } + + if (game?.status === "cancelled") { + return ( + <> + + + + ); + } + + if (gameDetails && gameDetails.repacks.length) { + return ( + <> + {toggleGameOnLibraryButton} + + + ); + } + + return toggleGameOnLibraryButton; +} diff --git a/src/renderer/src/pages/game-details/hero-panel.css.ts b/src/renderer/src/pages/game-details/hero-panel.css.ts new file mode 100644 index 00000000..6824f9a4 --- /dev/null +++ b/src/renderer/src/pages/game-details/hero-panel.css.ts @@ -0,0 +1,32 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const panel = style({ + width: "100%", + height: "72px", + padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`, + display: "flex", + alignItems: "center", + justifyContent: "space-between", + transition: "all ease 0.2s", + borderBottom: `solid 1px ${vars.color.borderColor}`, + boxShadow: "0px 0px 15px 0px #000000", +}); + +export const content = style({ + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT}px`, + fontSize: vars.size.bodyFontSize, +}); + +export const actions = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, +}); + +export const downloadDetailsRow = style({ + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + alignItems: "flex-end", +}); diff --git a/src/renderer/src/pages/game-details/hero-panel.tsx b/src/renderer/src/pages/game-details/hero-panel.tsx new file mode 100644 index 00000000..09a04970 --- /dev/null +++ b/src/renderer/src/pages/game-details/hero-panel.tsx @@ -0,0 +1,196 @@ +import { format } from "date-fns"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { useDownload } from "@renderer/hooks"; +import type { Game, ShopDetails } from "@types"; + +import { formatDownloadProgress } from "@renderer/helpers"; +import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; +import * as styles from "./hero-panel.css"; +import { useDate } from "@renderer/hooks/use-date"; +import { formatBytes } from "@renderer/utils"; +import { HeroPanelActions } from "./hero-panel-actions"; + +export interface HeroPanelProps { + game: Game | null; + gameDetails: ShopDetails | null; + color: string; + isGamePlaying: boolean; + openRepacksModal: () => void; + getGame: () => void; +} + +export function HeroPanel({ + game, + gameDetails, + color, + openRepacksModal, + getGame, + isGamePlaying, +}: HeroPanelProps) { + const { t } = useTranslation("game_details"); + + const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false); + const [lastTimePlayed, setLastTimePlayed] = useState(""); + + const { formatDistance } = useDate(); + + const { + game: gameDownloading, + isDownloading, + progress, + eta, + numPeers, + numSeeds, + isGameDeleting, + } = useDownload(); + const isGameDownloading = isDownloading && gameDownloading?.id === game?.id; + + const updateLastTimePlayed = useCallback(() => { + setLastTimePlayed( + formatDistance(game.lastTimePlayed, new Date(), { + addSuffix: true, + }) + ); + }, [game?.lastTimePlayed, formatDistance]); + + useEffect(() => { + if (game?.lastTimePlayed) { + updateLastTimePlayed(); + + const interval = setInterval(() => { + updateLastTimePlayed(); + }, 1000); + + return () => { + clearInterval(interval); + }; + } + }, [game?.lastTimePlayed, updateLastTimePlayed]); + + const finalDownloadSize = useMemo(() => { + if (!game) return "N/A"; + if (game.fileSize) return formatBytes(game.fileSize); + + if (gameDownloading?.fileSize && isGameDownloading) + return formatBytes(gameDownloading.fileSize); + + return game.repack?.fileSize ?? "N/A"; + }, [game, isGameDownloading, gameDownloading]); + + const getInfo = () => { + if (!gameDetails) return null; + + if (isGameDeleting(game?.id)) { + return

{t("deleting")}

; + } + + if (isGameDownloading) { + return ( + <> +

+ {progress} + {eta && {t("eta", { eta })}} +

+ + {gameDownloading?.status !== "downloading" ? ( + <> +

{t(gameDownloading?.status)}

+ {eta && {t("eta", { eta })}} + + ) : ( +

+ {formatBytes(gameDownloading?.bytesDownloaded)} /{" "} + {finalDownloadSize} + + {numPeers} peers / {numSeeds} seeds + +

+ )} + + ); + } + + if (game?.status === "paused") { + return ( + <> +

+ {t("paused_progress", { + progress: formatDownloadProgress(game.progress), + })} +

+

+ {formatBytes(game.bytesDownloaded)} / {finalDownloadSize} +

+ + ); + } + + if (game?.status === "seeding" || (game && !game.status)) { + if (!game.lastTimePlayed) { + return

{t("not_played_yet", { title: game.title })}

; + } + + return ( + <> +

+ {t("play_time", { + amount: formatDistance(0, game.playTimeInMilliseconds), + })} +

+ + {isGamePlaying ? ( +

{t("playing_now")}

+ ) : ( +

+ {t("last_time_played", { + period: lastTimePlayed, + })} +

+ )} + + ); + } + + const [latestRepack] = gameDetails.repacks; + + if (latestRepack) { + const lastUpdate = format(latestRepack.uploadDate!, "dd/MM/yyyy"); + const repacksCount = gameDetails.repacks.length; + + return ( + <> +

{t("updated_at", { updated_at: lastUpdate })}

+

{t("download_options", { count: repacksCount })}

+ + ); + } + + return

{t("no_downloads")}

; + }; + + return ( + <> + setShowBinaryNotFoundModal(false)} + /> + +
+
{getInfo()}
+
+ setShowBinaryNotFoundModal(true)} + isGamePlaying={isGamePlaying} + isGameDownloading={isGameDownloading} + /> +
+
+ + ); +} diff --git a/src/renderer/src/pages/game-details/how-long-to-beat-section.tsx b/src/renderer/src/pages/game-details/how-long-to-beat-section.tsx new file mode 100644 index 00000000..c46afbf6 --- /dev/null +++ b/src/renderer/src/pages/game-details/how-long-to-beat-section.tsx @@ -0,0 +1,69 @@ +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import type { HowLongToBeatCategory } from "@types"; +import { useTranslation } from "react-i18next"; +import { vars } from "../../theme.css"; +import * as styles from "./game-details.css"; + +const durationTranslation: Record = { + Hours: "hours", + Mins: "minutes", +}; + +export interface HowLongToBeatSectionProps { + howLongToBeatData: HowLongToBeatCategory[] | null; + isLoading: boolean; +} + +export function HowLongToBeatSection({ + howLongToBeatData, + isLoading, +}: HowLongToBeatSectionProps) { + const { t } = useTranslation("game_details"); + + const getDuration = (duration: string) => { + const [value, unit] = duration.split(" "); + return `${value} ${t(durationTranslation[unit])}`; + }; + + if (!howLongToBeatData && !isLoading) return null; + + return ( + +
+

HowLongToBeat

+
+ +
    + {howLongToBeatData + ? howLongToBeatData.map((category) => ( +
  • +

    + {category.title} +

    + +

    + {getDuration(category.duration)} +

    + + {category.accuracy !== "00" && ( + + {t("accuracy", { accuracy: category.accuracy })} + + )} +
  • + )) + : Array.from({ length: 4 }).map((_, index) => ( + + ))} +
+
+ ); +} diff --git a/src/renderer/src/pages/game-details/repacks-modal.css.ts b/src/renderer/src/pages/game-details/repacks-modal.css.ts new file mode 100644 index 00000000..4e15a63a --- /dev/null +++ b/src/renderer/src/pages/game-details/repacks-modal.css.ts @@ -0,0 +1,18 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const repacks = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, + flexDirection: "column", +}); + +export const repackButton = style({ + display: "flex", + textAlign: "left", + flexDirection: "column", + alignItems: "flex-start", + gap: `${SPACING_UNIT}px`, + color: vars.color.bodyText, + padding: `${SPACING_UNIT * 2}px`, +}); diff --git a/src/renderer/src/pages/game-details/repacks-modal.tsx b/src/renderer/src/pages/game-details/repacks-modal.tsx new file mode 100644 index 00000000..da74e53e --- /dev/null +++ b/src/renderer/src/pages/game-details/repacks-modal.tsx @@ -0,0 +1,101 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { Button, Modal, TextField } from "@renderer/components"; +import type { GameRepack, ShopDetails } from "@types"; + +import * as styles from "./repacks-modal.css"; + +import type { DiskSpace } from "check-disk-space"; +import { format } from "date-fns"; +import { SPACING_UNIT } from "../../theme.css"; +import { formatBytes } from "@renderer/utils"; +import { useAppSelector } from "@renderer/hooks"; + +export interface RepacksModalProps { + visible: boolean; + gameDetails: ShopDetails; + startDownload: (repackId: number) => Promise; + onClose: () => void; +} + +export function RepacksModal({ + visible, + gameDetails, + startDownload, + onClose, +}: RepacksModalProps) { + const [downloadStarting, setDownloadStarting] = useState(false); + const [diskFreeSpace, setDiskFreeSpace] = useState(null); + const [filteredRepacks, setFilteredRepacks] = useState([]); + + const repackersFriendlyNames = useAppSelector( + (state) => state.repackersFriendlyNames.value + ); + + const { t } = useTranslation("game_details"); + + useEffect(() => { + setFilteredRepacks(gameDetails.repacks); + }, [gameDetails.repacks]); + + const getDiskFreeSpace = () => { + window.electron.getDiskFreeSpace().then((result) => { + setDiskFreeSpace(result); + }); + }; + + useEffect(() => { + getDiskFreeSpace(); + }, [visible]); + + const handleRepackClick = (repack: GameRepack) => { + setDownloadStarting(true); + startDownload(repack.id).finally(() => { + setDownloadStarting(false); + }); + }; + + const handleFilter: React.ChangeEventHandler = (event) => { + setFilteredRepacks( + gameDetails.repacks.filter((repack) => + repack.title + .toLowerCase() + .includes(event.target.value.toLocaleLowerCase()) + ) + ); + }; + + return ( + +
+ +
+ +
+ {filteredRepacks.map((repack) => ( + + ))} +
+
+ ); +} diff --git a/src/renderer/src/pages/home/catalogue-home.css.ts b/src/renderer/src/pages/home/catalogue-home.css.ts new file mode 100644 index 00000000..c9f00cd7 --- /dev/null +++ b/src/renderer/src/pages/home/catalogue-home.css.ts @@ -0,0 +1,26 @@ +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { SPACING_UNIT } from "../../theme.css"; + +export const catalogueCategories = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, +}); + +export const content = style({ + width: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 3}px`, + padding: `${SPACING_UNIT * 3}px`, + flex: "1", +}); + +export const cards = recipe({ + base: { + display: "grid", + gridTemplateColumns: "repeat(2, 1fr)", + gap: `${SPACING_UNIT * 2}px`, + transition: "all ease 0.2s", + }, +}); diff --git a/src/renderer/src/pages/home/home.css.ts b/src/renderer/src/pages/home/home.css.ts new file mode 100644 index 00000000..8caeffcf --- /dev/null +++ b/src/renderer/src/pages/home/home.css.ts @@ -0,0 +1,65 @@ +import { style } from "@vanilla-extract/css"; +import { SPACING_UNIT, vars } from "../../theme.css"; + +export const homeCategories = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, +}); + +export const homeHeader = style({ + display: "flex", + gap: `${SPACING_UNIT}px`, + justifyContent: "space-between", +}); + +export const content = style({ + width: "100%", + height: "100%", + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT * 3}px`, + padding: `${SPACING_UNIT * 3}px`, + flex: "1", + overflowY: "auto", +}); + +export const cards = style({ + display: "grid", + gridTemplateColumns: "repeat(1, 1fr)", + gap: `${SPACING_UNIT * 2}px`, + transition: "all ease 0.2s", + "@media": { + "(min-width: 768px)": { + gridTemplateColumns: "repeat(2, 1fr)", + }, + "(min-width: 1250px)": { + gridTemplateColumns: "repeat(3, 1fr)", + }, + "(min-width: 1600px)": { + gridTemplateColumns: "repeat(4, 1fr)", + }, + }, +}); + +export const cardSkeleton = style({ + width: "100%", + height: "180px", + boxShadow: "0px 0px 15px 0px #000000", + overflow: "hidden", + borderRadius: "4px", + transition: "all ease 0.2s", + zIndex: "1", + ":active": { + opacity: vars.opacity.active, + }, +}); + +export const noResults = style({ + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "center", + flexDirection: "column", + gap: "16px", + gridColumn: "1 / -1", +}); diff --git a/src/renderer/src/pages/home/home.tsx b/src/renderer/src/pages/home/home.tsx new file mode 100644 index 00000000..da561016 --- /dev/null +++ b/src/renderer/src/pages/home/home.tsx @@ -0,0 +1,143 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate, useSearchParams } from "react-router-dom"; + +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; + +import { Button, GameCard, Hero } from "@renderer/components"; +import type { CatalogueCategory, CatalogueEntry } from "@types"; + +import starsAnimation from "@renderer/assets/lottie/stars.json"; + +import * as styles from "./home.css"; +import { vars } from "../../theme.css"; +import Lottie from "lottie-react"; + +const categories: CatalogueCategory[] = ["trending", "recently_added"]; + +export function Home() { + const { t } = useTranslation("home"); + const navigate = useNavigate(); + + const [isLoading, setIsLoading] = useState(false); + const [isLoadingRandomGame, setIsLoadingRandomGame] = useState(false); + const randomGameObjectID = useRef(null); + + const [searchParams] = useSearchParams(); + + const [catalogue, setCatalogue] = useState< + Record + >({ + trending: [], + recently_added: [], + }); + + const getCatalogue = useCallback((category: CatalogueCategory) => { + setIsLoading(true); + + window.electron + .getCatalogue(category) + .then((catalogue) => { + setCatalogue((prev) => ({ ...prev, [category]: catalogue })); + }) + .catch(() => {}) + .finally(() => { + setIsLoading(false); + }); + }, []); + + const currentCategory = searchParams.get("category") || categories[0]; + + const handleSelectCategory = (category: CatalogueCategory) => { + if (category !== currentCategory) { + getCatalogue(category); + navigate(`/?category=${category}`, { replace: true }); + } + }; + + const getRandomGame = useCallback(() => { + setIsLoadingRandomGame(true); + + window.electron + .getRandomGame() + .then((objectID) => { + randomGameObjectID.current = objectID; + }) + .finally(() => { + setIsLoadingRandomGame(false); + }); + }, []); + + const handleRandomizerClick = () => { + const searchParams = new URLSearchParams({ + fromRandomizer: "1", + }); + + navigate( + `/game/steam/${randomGameObjectID.current}?${searchParams.toString()}` + ); + }; + + useEffect(() => { + setIsLoading(true); + getCatalogue(currentCategory as CatalogueCategory); + getRandomGame(); + }, [getCatalogue, currentCategory, getRandomGame]); + + return ( + +
+

{t("featured")}

+ + + +
+
+ {categories.map((category) => ( + + ))} +
+ + +
+ +

{t(currentCategory)}

+ +
+ {isLoading + ? Array.from({ length: 12 }).map((_, index) => ( + + )) + : catalogue[currentCategory as CatalogueCategory].map((result) => ( + + navigate(`/game/${result.shop}/${result.objectID}`) + } + /> + ))} +
+
+
+ ); +} diff --git a/src/renderer/src/pages/home/search-results.tsx b/src/renderer/src/pages/home/search-results.tsx new file mode 100644 index 00000000..65118b17 --- /dev/null +++ b/src/renderer/src/pages/home/search-results.tsx @@ -0,0 +1,86 @@ +import { GameCard } from "@renderer/components"; +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; + +import type { CatalogueEntry } from "@types"; + +import type { DebouncedFunc } from "lodash"; +import { debounce } from "lodash-es"; + +import { InboxIcon } from "@primer/octicons-react"; +import { clearSearch } from "@renderer/features"; +import { useAppDispatch } from "@renderer/hooks"; +import { vars } from "../../theme.css"; +import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import * as styles from "./home.css"; + +export function SearchResults() { + const dispatch = useAppDispatch(); + + const { t } = useTranslation("home"); + const [searchParams] = useSearchParams(); + + const [searchResults, setSearchResults] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const debouncedFunc = useRef void | null>>(null); + + const navigate = useNavigate(); + + const handleGameClick = (game: CatalogueEntry) => { + dispatch(clearSearch()); + navigate(`/game/${game.shop}/${game.objectID}`); + }; + + useEffect(() => { + setIsLoading(true); + if (debouncedFunc.current) debouncedFunc.current.cancel(); + + debouncedFunc.current = debounce(() => { + window.electron + .searchGames(searchParams.get("query")) + .then((results) => { + setSearchResults(results); + }) + .finally(() => { + setIsLoading(false); + }); + }, 300); + + debouncedFunc.current(); + }, [searchParams, dispatch]); + + return ( + +
+
+ {isLoading && + Array.from({ length: 12 }).map((_, index) => ( + + ))} + + {!isLoading && searchResults.length > 0 && ( + <> + {searchResults.map((game) => ( + handleGameClick(game)} + /> + ))} + + )} +
+ + {!isLoading && searchResults.length === 0 && ( +
+ + +

{t("no_results")}

+
+ )} +
+
+ ); +} diff --git a/src/renderer/src/pages/index.ts b/src/renderer/src/pages/index.ts new file mode 100644 index 00000000..fd883fbf --- /dev/null +++ b/src/renderer/src/pages/index.ts @@ -0,0 +1,6 @@ +export * from "./home/home"; +export * from "./game-details/game-details"; +export * from "./downloads/downloads"; +export * from "./home/search-results"; +export * from "./settings/settings"; +export * from "./catalogue/catalogue"; diff --git a/src/renderer/src/pages/patch-notes/patch-notes-skeleton.tsx b/src/renderer/src/pages/patch-notes/patch-notes-skeleton.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/renderer/src/pages/settings/settings.css.ts b/src/renderer/src/pages/settings/settings.css.ts new file mode 100644 index 00000000..506b9574 --- /dev/null +++ b/src/renderer/src/pages/settings/settings.css.ts @@ -0,0 +1,26 @@ +import { SPACING_UNIT, vars } from "../../theme.css"; +import { style } from "@vanilla-extract/css"; + +export const container = style({ + padding: "24px", + width: "100%", + display: "flex", +}); + +export const content = style({ + backgroundColor: vars.color.background, + width: "100%", + height: "100%", + padding: `${SPACING_UNIT * 3}px`, + border: `solid 1px ${vars.color.borderColor}`, + boxShadow: "0px 0px 15px 0px #000000", + borderRadius: "8px", + gap: `${SPACING_UNIT * 2}px`, + display: "flex", + flexDirection: "column", +}); + +export const downloadsPathField = style({ + display: "flex", + gap: `${SPACING_UNIT * 2}px`, +}); diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx new file mode 100644 index 00000000..47bd604b --- /dev/null +++ b/src/renderer/src/pages/settings/settings.tsx @@ -0,0 +1,113 @@ +import { useEffect, useState } from "react"; +import { Button, CheckboxField, TextField } from "@renderer/components"; + +import * as styles from "./settings.css"; +import { useTranslation } from "react-i18next"; +import { UserPreferences } from "@types"; + +export function Settings() { + const [form, setForm] = useState({ + downloadsPath: "", + downloadNotificationsEnabled: false, + repackUpdatesNotificationsEnabled: false, + telemetryEnabled: false, + }); + + const { t } = useTranslation("settings"); + + useEffect(() => { + Promise.all([ + window.electron.getDefaultDownloadsPath(), + window.electron.getUserPreferences(), + ]).then(([path, userPreferences]) => { + setForm({ + downloadsPath: userPreferences?.downloadsPath || path, + downloadNotificationsEnabled: + userPreferences?.downloadNotificationsEnabled, + repackUpdatesNotificationsEnabled: + userPreferences?.repackUpdatesNotificationsEnabled, + telemetryEnabled: userPreferences?.telemetryEnabled, + }); + }); + }, []); + + const updateUserPreferences = ( + field: T, + value: UserPreferences[T] + ) => { + setForm((prev) => ({ ...prev, [field]: value })); + + window.electron.updateUserPreferences({ + [field]: value, + }); + }; + + const handleChooseDownloadsPath = async () => { + const { filePaths } = await window.electron.showOpenDialog({ + defaultPath: form.downloadsPath, + properties: ["openDirectory"], + }); + + if (filePaths && filePaths.length > 0) { + const path = filePaths[0]; + updateUserPreferences("downloadsPath", path); + } + }; + + return ( +
+
+
+ + + +
+ +

{t("notifications")}

+ + + updateUserPreferences( + "downloadNotificationsEnabled", + !form.downloadNotificationsEnabled + ) + } + /> + + + updateUserPreferences( + "repackUpdatesNotificationsEnabled", + !form.repackUpdatesNotificationsEnabled + ) + } + /> + +

{t("telemetry")}

+ + + updateUserPreferences("telemetryEnabled", !form.telemetryEnabled) + } + /> +
+
+ ); +} diff --git a/src/renderer/src/pages/shared-modals/binary-not-found-modal.tsx b/src/renderer/src/pages/shared-modals/binary-not-found-modal.tsx new file mode 100644 index 00000000..ec68b512 --- /dev/null +++ b/src/renderer/src/pages/shared-modals/binary-not-found-modal.tsx @@ -0,0 +1,25 @@ +import { Modal } from "@renderer/components"; +import { useTranslation } from "react-i18next"; + +interface BinaryNotFoundModalProps { + visible: boolean; + onClose: () => void; +} + +export const BinaryNotFoundModal = ({ + visible, + onClose, +}: BinaryNotFoundModalProps) => { + const { t } = useTranslation("binary_not_found_modal"); + + return ( + + {t("instructions")} + + ); +}; diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts new file mode 100644 index 00000000..e2bb9baa --- /dev/null +++ b/src/renderer/src/store.ts @@ -0,0 +1,23 @@ +import { configureStore } from "@reduxjs/toolkit"; +import { + downloadSlice, + windowSlice, + librarySlice, + repackersFriendlyNamesSlice, + searchSlice, + userPreferencesSlice, +} from "@renderer/features"; + +export const store = configureStore({ + reducer: { + search: searchSlice.reducer, + repackersFriendlyNames: repackersFriendlyNamesSlice.reducer, + window: windowSlice.reducer, + library: librarySlice.reducer, + userPreferences: userPreferencesSlice.reducer, + download: downloadSlice.reducer, + }, +}); + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/src/renderer/src/theme.css.ts b/src/renderer/src/theme.css.ts new file mode 100644 index 00000000..b11f1acb --- /dev/null +++ b/src/renderer/src/theme.css.ts @@ -0,0 +1,19 @@ +import { createTheme } from "@vanilla-extract/css"; + +export const SPACING_UNIT = 8; + +export const [themeClass, vars] = createTheme({ + color: { + background: "#1c1c1c", + darkBackground: "#151515", + bodyText: "#8e919b", + borderColor: "rgba(255, 255, 255, 0.1)", + }, + opacity: { + disabled: "0.5", + active: "0.7", + }, + size: { + bodyFontSize: "14px", + }, +}); diff --git a/src/renderer/src/utils/format-bytes.ts b/src/renderer/src/utils/format-bytes.ts new file mode 100644 index 00000000..b052b43b --- /dev/null +++ b/src/renderer/src/utils/format-bytes.ts @@ -0,0 +1,15 @@ +const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + +export const formatBytes = (bytes: number): string => { + if (!Number.isFinite(bytes) || isNaN(bytes) || bytes <= 0) { + return `0 ${FORMAT[0]}`; + } + + const byteKBase = 1024; + + const base = Math.floor(Math.log(bytes) / Math.log(byteKBase)); + + const formatedByte = bytes / byteKBase ** base; + + return `${Math.trunc(formatedByte * 10) / 10} ${FORMAT[base]}`; +}; diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts new file mode 100644 index 00000000..7a828a7f --- /dev/null +++ b/src/renderer/src/utils/index.ts @@ -0,0 +1 @@ +export * from "./format-bytes"; diff --git a/src/renderer/src/vite-env.d.ts b/src/renderer/src/vite-env.d.ts new file mode 100644 index 00000000..5208138c --- /dev/null +++ b/src/renderer/src/vite-env.d.ts @@ -0,0 +1,10 @@ +/// +/// + +interface ImportMetaEnv { + readonly RENDERER_VITE_SENTRY_DSN: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..768f110b --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,114 @@ +export type GameShop = "steam" | "epic"; +export type CatalogueCategory = "recently_added" | "trending"; + +export interface SteamGenre { + id: string; + name: string; +} + +export interface SteamScreenshot { + id: number; + path_thumbnail: string; + path_full: string; +} + +export interface SteamAppDetails { + name: string; + detailed_description: string; + about_the_game: string; + short_description: string; + publishers: string[]; + genres: SteamGenre[]; + screenshots: SteamScreenshot[]; + pc_requirements: { + minimum: string; + recommended: string; + }; + mac_requirements: { + minimum: string; + recommended: string; + }; + linux_requirmenets: { + minimum: string; + recommended: string; + }; + release_date: { + coming_soon: boolean; + date: string; + }; +} + +export type ShopDetails = SteamAppDetails & { + objectID: string; + repacks: GameRepack[]; +}; + +export interface GameRepack { + id: number; + title: string; + magnet: string; + page: number; + repacker: string; + fileSize: string | null; + uploadDate: Date | string | null; + createdAt: Date; + updatedAt: Date; +} + +export interface TorrentFile { + path: string; + length: number; +} + +/* Used by the catalogue */ +export interface CatalogueEntry { + objectID: string; + shop: GameShop; + title: string; + /* Epic Games covers cannot be guessed with objectID */ + cover: string; + repacks: GameRepack[]; +} + +/* Used by the library */ +export interface Game extends Omit { + id: number; + title: string; + iconUrl: string; + status: string; + folderName: string; + downloadPath: string | null; + repacks: GameRepack[]; + repack: GameRepack; + progress: number; + fileVerificationProgress: number; + bytesDownloaded: number; + playTimeInMilliseconds: number; + executablePath: string | null; + lastTimePlayed: Date | null; + fileSize: number; + createdAt: Date; + updatedAt: Date; +} + +export interface TorrentProgress { + downloadSpeed: number; + timeRemaining: number; + numPeers: number; + numSeeds: number; + game: Omit; +} + +export interface UserPreferences { + downloadsPath: string | null; + language: string; + downloadNotificationsEnabled: boolean; + repackUpdatesNotificationsEnabled: boolean; + telemetryEnabled: boolean; +} + +export interface HowLongToBeatCategory { + title: string; + duration: string; + accuracy: string; +} diff --git a/torrent-client/__pycache__/fifo.cpython-39.pyc b/torrent-client/__pycache__/fifo.cpython-39.pyc new file mode 100644 index 00000000..30225a1b Binary files /dev/null and b/torrent-client/__pycache__/fifo.cpython-39.pyc differ diff --git a/torrent-client/fifo.py b/torrent-client/fifo.py new file mode 100644 index 00000000..189ce125 --- /dev/null +++ b/torrent-client/fifo.py @@ -0,0 +1,32 @@ +import platform + +class Fifo: + socket_handle = None + + def __init__(self, path: str): + if platform.system() == "Windows": + import win32file + + self.socket_handle = win32file.CreateFile(path, win32file.GENERIC_READ | win32file.GENERIC_WRITE, + 0, None, win32file.OPEN_EXISTING, win32file.FILE_ATTRIBUTE_NORMAL, None) + else: + import socket + self.socket_handle = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket_handle.connect(path) + + def recv(self, bufSize: int): + if platform.system() == "Windows": + import win32file + + result, data = win32file.ReadFile(self.socket_handle, bufSize) + return data + else: + return self.socket_handle.recv(bufSize) + + def send_message(self, msg: str): + if platform.system() == "Windows": + import win32file + + win32file.WriteFile(self.socket_handle, bytes(msg, "utf-8")) + else: + self.socket_handle.send(bytes(msg, "utf-8")) diff --git a/torrent-client/main.py b/torrent-client/main.py new file mode 100644 index 00000000..99c51538 --- /dev/null +++ b/torrent-client/main.py @@ -0,0 +1,103 @@ +import libtorrent as lt +import sys +from fifo import Fifo +import json +import threading +import time + +torrent_port = sys.argv[1] +read_sock_path = sys.argv[2] +write_sock_path = sys.argv[3] + +session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=torrent_port)}) +read_fifo = Fifo(read_sock_path) +write_fifo = Fifo(write_sock_path) + +torrent_handle = None +downloading_game_id = 0 + +def get_eta(status): + remaining_bytes = status.total_wanted - status.total_wanted_done + + if remaining_bytes >= 0 and status.download_rate > 0: + return (remaining_bytes / status.download_rate) * 1000 + else: + return 1 + +def start_download(game_id: int, magnet: str, save_path: str): + global torrent_handle + global downloading_game_id + + params = {'url': magnet, 'save_path': save_path} + torrent_handle = session.add_torrent(params) + downloading_game_id = game_id + torrent_handle.set_flags(lt.torrent_flags.auto_managed) + torrent_handle.resume() + +def pause_download(): + global downloading_game_id + + if torrent_handle: + torrent_handle.pause() + torrent_handle.unset_flags(lt.torrent_flags.auto_managed) + downloading_game_id = 0 + +def cancel_download(): + global downloading_game_id + global torrent_handle + + if torrent_handle: + torrent_handle.pause() + session.remove_torrent(torrent_handle) + torrent_handle = None + downloading_game_id = 0 + +def get_download_updates(): + while True: + if downloading_game_id == 0: + time.sleep(0.5) + continue + + status = torrent_handle.status() + info = torrent_handle.get_torrent_info() + + write_fifo.send_message(json.dumps({ + 'folderName': info.name() if info else "", + 'fileSize': info.total_size() if info else 0, + 'gameId': downloading_game_id, + 'progress': status.progress, + 'downloadSpeed': status.download_rate, + 'timeRemaining': get_eta(status), + 'numPeers': status.num_peers, + 'numSeeds': status.num_seeds, + 'status': status.state, + 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download, + })) + + if status.progress == 1: + cancel_download() + + time.sleep(0.5) + +def listen_to_socket(): + while True: + msg = read_fifo.recv(1024 * 2) + payload = json.loads(msg.decode("utf-8")) + + if payload['action'] == "start": + start_download(payload['game_id'], payload['magnet'], payload['save_path']) + continue + + if payload['action'] == "pause": + pause_download() + continue + + if payload['action'] == "cancel": + cancel_download() + +if __name__ == "__main__": + p1 = threading.Thread(target=get_download_updates) + p2 = threading.Thread(target=listen_to_socket) + + p1.start() + p2.start() diff --git a/torrent-client/setup.py b/torrent-client/setup.py new file mode 100644 index 00000000..b26513b1 --- /dev/null +++ b/torrent-client/setup.py @@ -0,0 +1,20 @@ +from cx_Freeze import setup, Executable + +# Dependencies are automatically detected, but it might need fine tuning. +build_exe_options = { + "packages": ["libtorrent"], + "build_exe": "resources/dist/hydra-download-manager", + "include_msvcr": True +} + +setup( + name="hydra-download-manager", + version="0.1", + description="Hydra Torrent Client", + options={"build_exe": build_exe_options}, + executables=[Executable( + "torrent-client/main.py", + target_name="hydra-download-manager", + icon="images/icon.ico" + )] +) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..31bac6e9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..29fcd95c --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@electron-toolkit/tsconfig/tsconfig.node.json", + "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*", "src/locales/index.ts"], + "compilerOptions": { + "module": "ESNext", + "composite": true, + "types": ["electron-vite/node"], + "baseUrl": ".", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "paths": { + "@main/*": ["src/main/*"], + "@renderer/*": ["src/renderer/*"], + "@types": ["src/types/index.ts"], + "@locales": ["src/locales/index.ts"] + } + } +} diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 00000000..a888cf67 --- /dev/null +++ b/tsconfig.web.json @@ -0,0 +1,22 @@ +{ + "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/locales/index.ts" + ], + "compilerOptions": { + "composite": true, + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@renderer/*": [ + "src/renderer/src/*" + ], + "@types": ["src/types/index.ts"], + "@locales": ["src/locales/index.ts"] + } + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..09b4eabf --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5891 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"7zip-bin@~5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.2.0.tgz#7a03314684dd6572b7dfa89e68ce31d60286854d" + integrity sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A== + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/compat-data@^7.23.5": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + +"@babel/core@^7.21.3", "@babel/core@^7.23.5", "@babel/core@^7.23.9": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" + integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.4" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.24.4" + "@babel/parser" "^7.24.4" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.24.1", "@babel/generator@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.4.tgz#1fc55532b88adf952025d5d2d1e71f946cb1c498" + integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== + dependencies: + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" + integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.4.tgz#dc00907fd0d95da74563c142ef4cd21f2cb856b6" + integrity sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" + integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== + +"@babel/plugin-syntax-typescript@^7.23.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" + integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz#2bf263617060c9cc45bcdbf492b8cc805082bf27" + integrity sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-react-jsx-self@^7.23.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.1.tgz#a21d866d8167e752c6a7c4555dba8afcdfce6268" + integrity sha512-kDJgnPujTmAZ/9q2CN4m2/lRsUUPDvsG3+tSHWUJIzMGTt5U/b/fwWd3RO3n+5mjLrsBrVa5eKFRVSQbi3dF1w== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-react-jsx-source@^7.23.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz#a2dedb12b09532846721b5df99e52ef8dc3351d0" + integrity sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + +"@babel/traverse@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== + dependencies: + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.1" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@develar/schema-utils@~2.6.5": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6" + integrity sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig== + dependencies: + ajv "^6.12.0" + ajv-keywords "^3.4.1" + +"@electron-toolkit/eslint-config-prettier@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@electron-toolkit/eslint-config-prettier/-/eslint-config-prettier-2.0.0.tgz#a258a28fc0a09b7f910fd71e0aa18ea6ee440735" + integrity sha512-L+uG1FvJcAZkPZpSi6B1pmdpyJFyOxWDTjr1Vs47vSryxv/EX1Ch6o4HVsachlDq3fMEkDgojuP2F3ZvVZMoLw== + dependencies: + eslint-config-prettier "^9.1.0" + eslint-plugin-prettier "^5.0.1" + +"@electron-toolkit/eslint-config-ts@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@electron-toolkit/eslint-config-ts/-/eslint-config-ts-1.0.1.tgz#ee5d5e754871568fe2698b0f18d063ab04ef6092" + integrity sha512-6RkoGE/W/WVlDozy/u6/lo5THNP5CCAPxdKpdQuYaopUfObs3o+JyxdlSb45imh+nL8nv875970EoyplntXaug== + dependencies: + "@typescript-eslint/eslint-plugin" "^6.14.0" + "@typescript-eslint/parser" "^6.14.0" + +"@electron-toolkit/preload@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@electron-toolkit/preload/-/preload-3.0.1.tgz#8bae193fd851f3d38c56eec13a5dd602744e8064" + integrity sha512-EzoQmpK8jqqU8YnM5jRe0GJjGVJPke2KtANqz8QtN2JPT96ViOvProBdK5C6riCm0j1T8jjAGVQCZLQy9OVoIA== + +"@electron-toolkit/tsconfig@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@electron-toolkit/tsconfig/-/tsconfig-1.0.1.tgz#78048d3178dd7a6a573590e23240f0764b0c14af" + integrity sha512-M0Mol3odspvtCuheyujLNAW7bXq7KFNYVMRtpjFa4ZfES4MuklXBC7Nli/omvc+PRKlrklgAGx3l4VakjNo8jg== + +"@electron-toolkit/utils@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@electron-toolkit/utils/-/utils-3.0.0.tgz#74626893d93025eacba086d497b615cf927d42c4" + integrity sha512-GaXHDhiT7KCvMJjXdp/QqpYinq69T/Pdl49Z1XLf8mKGf63dnsODMWyrmIjEQ0z/vG7dO8qF3fvmI6Eb2lUNZA== + +"@electron/asar@^3.2.1": + version "3.2.9" + resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.9.tgz#7b3a1fd677b485629f334dd80ced8c85353ba7e7" + integrity sha512-Vu2P3X2gcZ3MY9W7yH72X9+AMXwUQZEJBrsPIbX0JsdllLtoh62/Q8Wg370/DawIEVKOyfD6KtTLo645ezqxUA== + dependencies: + commander "^5.0.0" + glob "^7.1.6" + minimatch "^3.0.4" + +"@electron/get@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" + integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== + 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" + optionalDependencies: + global-agent "^3.0.0" + +"@electron/notarize@2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@electron/notarize/-/notarize-2.2.1.tgz#d0aa6bc43cba830c41bfd840b85dbe0e273f59fe" + integrity sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg== + dependencies: + debug "^4.1.1" + fs-extra "^9.0.1" + promise-retry "^2.0.1" + +"@electron/osx-sign@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@electron/osx-sign/-/osx-sign-1.0.5.tgz#0af7149f2fce44d1a8215660fd25a9fb610454d8" + integrity sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww== + 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" + +"@electron/universal@1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-1.5.1.tgz#f338bc5bcefef88573cf0ab1d5920fac10d06ee5" + integrity sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw== + dependencies: + "@electron/asar" "^3.2.1" + "@malept/cross-spawn-promise" "^1.1.0" + debug "^4.3.1" + dir-compare "^3.0.0" + fs-extra "^9.0.1" + minimatch "^3.0.4" + plist "^3.0.4" + +"@emotion/hash@^0.9.0": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + +"@esbuild/aix-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" + integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== + +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" + integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" + integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" + integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + +"@esbuild/darwin-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" + integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== + +"@esbuild/darwin-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" + integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== + +"@esbuild/darwin-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" + integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== + +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" + integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" + integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" + integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" + integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" + integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" + integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" + integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" + integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" + integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" + integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" + integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" + integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" + integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" + integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" + integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" + integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" + integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@fontsource/fira-mono@^5.0.13": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@fontsource/fira-mono/-/fira-mono-5.0.13.tgz#2aa677938f0759b8d368bddd3c7ec299ee0c17af" + integrity sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg== + +"@fontsource/fira-sans@^5.0.20": + version "5.0.20" + resolved "https://registry.yarnpkg.com/@fontsource/fira-sans/-/fira-sans-5.0.20.tgz#92d22b1289f932db1bca7bd20902544fb9706252" + integrity sha512-inmUjoKPrgnO4uUaZTAgP0b6YdhDfA52axHXvdTwgLvwd2kn3ZgK52UZoxD0VnrvTOjLA/iE4oC0tNtz4nyb5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@malept/cross-spawn-promise@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" + integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== + dependencies: + cross-spawn "^7.0.1" + +"@malept/flatpak-bundler@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz#e8a32c30a95d20c2b1bb635cc580981a06389858" + integrity sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q== + dependencies: + debug "^4.1.1" + fs-extra "^9.0.0" + lodash "^4.17.15" + tmp-promise "^3.0.2" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + +"@primer/octicons-react@^19.9.0": + version "19.9.0" + resolved "https://registry.yarnpkg.com/@primer/octicons-react/-/octicons-react-19.9.0.tgz#eb90a9b47da924c0ab33055d050e6ea21104d219" + integrity sha512-Uk4XrHyfylyfzZN9d8VFjF8FpfYHEyT4sabw+9+oP+GWAJHhPvNPTz6gXvUzJZmoblAvgcTrDslIPjz8zMh76w== + +"@reduxjs/toolkit@^2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.2.3.tgz#5ce71cbf162f98c5dafb49bd3f1e11c5486ab9c4" + integrity sha512-76dll9EnJXg4EVcI5YNxZA/9hSAmZsFqzMmNRHvIlzw2WS/twfcVX3ysYrWGJMClwEmChQFC4yRq74tn6fdzRA== + dependencies: + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.0.1" + +"@remix-run/router@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.3.tgz#d2509048d69dbb72d5389a14945339f1430b2d3c" + integrity sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w== + +"@rollup/pluginutils@^5.0.5": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rollup/rollup-android-arm-eabi@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.0.tgz#51e697b1fe08d3bc39d9f0341f22b1b6975a44df" + integrity sha512-4fDVBAfWYlw2CtYgHEWarAYSozTx5OYLsSM/cdGW7H51FwI10DaGnjKgdqWyWXY/VjugelzriCiKf1UdM20Bxg== + +"@rollup/rollup-android-arm64@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.0.tgz#e81fa0fd632ae1c9e6837565b929c95e7bfa0c5a" + integrity sha512-JltUBgsKgN108NO4/hj0B/dJYNrqqmdRCtUet5tFDi/w+0tvQP0FToyWBV4HKBcSX4cvFChrCyt5Rh4FX6M6QQ== + +"@rollup/rollup-darwin-arm64@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.0.tgz#0b4ee897790b299243603e5c3adce2932038be08" + integrity sha512-UwF7tkWf0roggMRv7Vrkof7VgX9tEZIc4vbaQl0/HNX3loWlcum+0ODp1Qsd8s7XvQGT+Zboxx1qxav3vq8YDw== + +"@rollup/rollup-darwin-x64@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.0.tgz#7f9bb7ca0f792bcf35d1bf10f0f4a848f9a2d527" + integrity sha512-RIY42wn6+Yb0qD29T7Dvm9/AhxrkGDf7X5dgI6rUFXR19+vCLh3u45yLcKOayu2ZQEba9rf/+BX3EggVwckiIw== + +"@rollup/rollup-linux-arm-gnueabihf@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.0.tgz#dae816a34b2ea0a29a8e2aacf939199b5ed8d565" + integrity sha512-r2TGCIKzqk8VwjOvW7sveledh6aPao131ejUfZNIyFlWBCruF4HOu51KtLArDa7LL6qKd0vkgxGX3/2NmYpWig== + +"@rollup/rollup-linux-arm-musleabihf@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.0.tgz#9adfa63cc89bf6fa91da38c8af5331c1decd7f1d" + integrity sha512-/QwaDp0RXQTtm25wQFSl02zEm9oveRXr9qAHbdxWCm9YG9dR8esqpyqzS/3GgHDm7jHktPNz9gTENfoUKRCcXQ== + +"@rollup/rollup-linux-arm64-gnu@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.0.tgz#49f4369d436a73fe0b4d4bc4d59cd89c79ee0b4a" + integrity sha512-iypHsz7YEfoyNL0iHbQ7B7pY6hpymvvMgFXXaMd5+WCtvJ9zqWPZKFmo78UeWzWNmTP9JtPiNIQt6efRxx/MNA== + +"@rollup/rollup-linux-arm64-musl@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.0.tgz#fd00d2b08a045a0321776921353cee3cc6383cc8" + integrity sha512-7UpYcO0uVeomnoL5MpQhrS0FT7xZUJrEXtKVLmps5bRA7x5AiA1PDuPnMbxcOBWjIM2HHIG1t3ndnRTVMIbk5A== + +"@rollup/rollup-linux-powerpc64le-gnu@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.0.tgz#d7a0b85cfca2aaf3adbf752569183b21233ac1de" + integrity sha512-FSuFy4/hOQy0lH135ifnElP/6dKoHcZGHovsaRY0jrfNRR2yjMnVYaqNHKGKy0b/1I8DkD/JtclgJfq7SPti1w== + +"@rollup/rollup-linux-riscv64-gnu@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.0.tgz#a419fb6e09baef0119dc7a59df8fda7208a2d672" + integrity sha512-qxAB8MiHuDI8jU0D+WI9Gym3fvUJHA/AjKRXxbEH921SB3AeKQStq1FKFA59dAoqqCArjJ1voXM/gMvgEc1q4Q== + +"@rollup/rollup-linux-s390x-gnu@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.0.tgz#693bac438527beefe3b061da847e3bfd6f6b571d" + integrity sha512-j/9yBgWFlNFBfG/S1M2zkBNLeLkNVG59T5c4tlmlrxU+XITWJ3aMVWdpcZ/+mu7auGZftAXueAgAE9mb4lAlag== + +"@rollup/rollup-linux-x64-gnu@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.0.tgz#41b43abe9073cfa4a02130e4d050fa3b10f82199" + integrity sha512-SjsBA1a9wrEleNneGEsR40HdxKdwCatyHC547o/XINqwPW4cqTYiNy/lL1WTJYWU/KgWIb8HH4SgmFStbWoBzw== + +"@rollup/rollup-linux-x64-musl@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.0.tgz#524046b68de9ccc938245628bb75ef6f07d515c8" + integrity sha512-YKCs7ghJZ5po6/qgfONiXyFKOKcTK4Kerzk/Kc89QK0JT94Qg4NurL+3Y3rZh5am2tu1OlvHPpBHQNBE8cFgJQ== + +"@rollup/rollup-win32-arm64-msvc@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.0.tgz#e9117384c4e340370777c5b5974c5cebf84c8807" + integrity sha512-+wtkF+z2nw0ZwwHji01wOW0loxFl24lBNxPtVAXtnPPDL9Ew0EhiCMOegXe/EAH3Zlr8Iw9tyPJXB3DltQLEyw== + +"@rollup/rollup-win32-ia32-msvc@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.0.tgz#1d9c9477138236a7588ee601513508854f674c69" + integrity sha512-7qLyKTL7Lf2g0B8bduETVAEI3WVUVwBRVcECojVevPNVAmi19IW1P2X+uMSwhmWNy36Q/qEvxXsfts1I8wpawg== + +"@rollup/rollup-win32-x64-msvc@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.0.tgz#0db109f8e1a604da9c9a86deebd84d8d040d6671" + integrity sha512-tkfxXt+7c3Ecgn7ln9NJPdBM+QKwQdmFFpgAP+FYhAuRS5y3tY8xeza82gFjbPpytkHmaQnVdMtuzbToCz2tuw== + +"@sentry-internal/feedback@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.110.0.tgz#7103a08cd6bfb43583087d7476a5f24c5857cc22" + integrity sha512-hrfWa3WkSOiBO5Srcr1j4kuGOlbsQic+REpLOofllVIs56DOo9+Aj9svxT+dcvZERv/nlFSV/E0BfGy9g08IEg== + dependencies: + "@sentry/core" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry-internal/feedback@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.111.0.tgz#c715e7e6a1877b60cd1f4dff85969660e0deff3f" + integrity sha512-xaKgPPDEirOan7c9HwzYA1KK87kRp/qfIx9ZKLOEtxwy6nqoMuSByGqSwm1Oqfcjpbd7y6/y+7Bw+69ZKNVLDQ== + dependencies: + "@sentry/core" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry-internal/replay-canvas@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.110.0.tgz#af21b56157f44c44a2eedf4326ef37f4ea440fa8" + integrity sha512-SNa+AfyfX+vc6Xw0pIfDsa5Qnc9cpexU6M2D19gadtVhmep7qoFBuhBVZrSv6BtdCxvrb5EyYsHYGfjQdIDcvg== + dependencies: + "@sentry/core" "7.110.0" + "@sentry/replay" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry-internal/replay-canvas@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.111.0.tgz#aa3cba0477f312cbf40eff4eabeaeda6221a55b6" + integrity sha512-3KPBIpiegTYmuVw9gA2aKuliAQONS3Ny1kJc9x5kz6XQGuLFxqlh6KzoCVaKfQJeq2WJqRNeR4KFFuNGuB3H8w== + dependencies: + "@sentry/core" "7.111.0" + "@sentry/replay" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry-internal/tracing@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.110.0.tgz#00f2086b0efb8dd5a67831074e52b176aa542d32" + integrity sha512-IIHHa9e/mE7uOMJfNELI8adyoELxOy6u6TNCn5t6fphmq84w8FTc9adXkG/FY2AQpglkIvlILojfMROFB2aaAQ== + dependencies: + "@sentry/core" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry-internal/tracing@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.111.0.tgz#b352df9f38009c5d306308a829a1dd9a57f084fd" + integrity sha512-CgXly8rsdu4loWVKi2RqpInH3C2cVBuaYsx4ZP5IJpzSinsUAMyyr3Pc0PZzCyoVpBBXGBGj/4HhFsY3q6Z0Vg== + dependencies: + "@sentry/core" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry/browser@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.110.0.tgz#40900d76a8f423a7163a594ec9267a2e0ebd8a5b" + integrity sha512-gIxedVm6ZgkjQfgCDgLWJgAsolq6OxV8hQ2j1+RaDL2RngvelFo/vlX5f2sD6EbjVp77Cri8u5GkMJF+v4p84g== + dependencies: + "@sentry-internal/feedback" "7.110.0" + "@sentry-internal/replay-canvas" "7.110.0" + "@sentry-internal/tracing" "7.110.0" + "@sentry/core" "7.110.0" + "@sentry/replay" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry/browser@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.111.0.tgz#29da73e7192eb5643d101c47922d7374e4cc88ed" + integrity sha512-x7S9XoJh+TbMnur4eBhPpCVo+p7udABBV2gQk+Iw6LP9e8EFKmGmNyl76vSsT6GeFJ7mwxDEKfuwbVoLBjIvHw== + dependencies: + "@sentry-internal/feedback" "7.111.0" + "@sentry-internal/replay-canvas" "7.111.0" + "@sentry-internal/tracing" "7.111.0" + "@sentry/core" "7.111.0" + "@sentry/replay" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry/core@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.110.0.tgz#2945d3ac0ef116ed313fbfb9da4f483b66fe5bca" + integrity sha512-g4suCQO94mZsKVaAbyD1zLFC5YSuBQCIPHXx9fdgtfoPib7BWjWWePkllkrvsKAv4u8Oq05RfnKOhOMRHpOKqg== + dependencies: + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry/core@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.111.0.tgz#54c9037a3b79b3623377dce1887b69b40670e201" + integrity sha512-/ljeMjZu8CSrLGrseBi/7S2zRIFsqMcvfyG6Nwgfc07J9nbHt8/MqouE1bXZfiaILqDBpK7BK9MLAAph4mkAWg== + dependencies: + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry/electron@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@sentry/electron/-/electron-4.23.0.tgz#dfb91734f64e78385b8dd005c4d312b64b16f35a" + integrity sha512-D6OkDJqoxHYrVgQbe6qwfjF0ZUtxNdO7q1E+fYK1vcoan3kWE2SbZJk3UqmqiTLHtR188bYB9z0TwsLM2/CRww== + dependencies: + "@sentry/browser" "7.110.0" + "@sentry/core" "7.110.0" + "@sentry/node" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + deepmerge "4.3.0" + tslib "^2.5.0" + +"@sentry/node@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.110.0.tgz#c75a7568e641ddb48d1636e62aaa37e9589e8806" + integrity sha512-YPfweCSzo/omnx5q1xOEZfI8Em3jnPqj7OM4ObXmoSKEK+kM1oUF3BTRzw5BJOaOCSTBFY1RAsGyfVIyrwxWnA== + dependencies: + "@sentry-internal/tracing" "7.110.0" + "@sentry/core" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry/react@^7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.111.0.tgz#ae9ef6d65f176ed5d5442723c2d278d0d0bd6120" + integrity sha512-8HVl4D5TsNLaIgXsOa1H+/aOcUHvRBsH+25OzxGpyS5Cu2gSlAQxfXKYxZ0axYoMc7TNkMNdmEhYN+CGV+NUZg== + dependencies: + "@sentry/browser" "7.111.0" + "@sentry/core" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + hoist-non-react-statics "^3.3.2" + +"@sentry/replay@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.110.0.tgz#e185c88cec573724b46b79ada7ef5a7098acd1b6" + integrity sha512-EEpGPf3iBJjWejvoxKLVMnLtLNwPTUxHJV1oxUkbcSi3B/tG5hW7LArYDjAcvkfa4VmA8JLCwj2vYU5MQ8tj6g== + dependencies: + "@sentry-internal/tracing" "7.110.0" + "@sentry/core" "7.110.0" + "@sentry/types" "7.110.0" + "@sentry/utils" "7.110.0" + +"@sentry/replay@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.111.0.tgz#6d21bddf2ec245db6eb2c471e81efd94364107ae" + integrity sha512-cSbI4A4hrO0sZ0ynvLQauPg8YyaDOQkhGkyvbws8W9WgfxR8X827bY9S0f1TPfgaFiVcKb0iRaAwyXHg3pyzOg== + dependencies: + "@sentry-internal/tracing" "7.111.0" + "@sentry/core" "7.111.0" + "@sentry/types" "7.111.0" + "@sentry/utils" "7.111.0" + +"@sentry/types@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.110.0.tgz#c3f252b008cab905097fc71e174191f20bdaf4f3" + integrity sha512-DqYBLyE8thC5P5MuPn+sj8tL60nCd/f5cerFFPcudn5nJ4Zs1eI6lKlwwyHYTEu5c4KFjCB0qql6kXfwAHmTyA== + +"@sentry/types@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.111.0.tgz#9c869c3c51d606041916765ba58f29de915707ac" + integrity sha512-Oti4pgQ55+FBHKKcHGu51ZUxO1u52G5iVNK4mbtAN+5ArSCy/2s1H8IDJiOMswn3acfUnCR0oB/QsbEgAPZ26g== + +"@sentry/utils@7.110.0": + version "7.110.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.110.0.tgz#68ef59359d608a1a6a7205b780196a042ad73ab2" + integrity sha512-VBsdLLN+5tf73fhf/Cm7JIsUJ6y9DkJj8h4I6Mxx0rszrvOyH6S5px40K+V4jdLBzMEvVinC7q2Cbf1YM18BSw== + dependencies: + "@sentry/types" "7.110.0" + +"@sentry/utils@7.111.0": + version "7.111.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.111.0.tgz#e006cc1e751b30ff5cf914c34eb143102e2e8c2d" + integrity sha512-CB5rz1EgCSwj3xoXogsCZ5pQtfERrURc/ItcCuoaijUhkD0iMq5MCNWMHW3mBsBrqx/Oba+XGvDu0t/5+SWwBg== + dependencies: + "@sentry/types" "7.111.0" + +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sqltools/formatter@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" + integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== + +"@svgr/babel-plugin-add-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" + integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== + +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" + integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== + +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" + integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" + integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== + +"@svgr/babel-plugin-svg-dynamic-title@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" + integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== + +"@svgr/babel-plugin-svg-em-dimensions@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" + integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== + +"@svgr/babel-plugin-transform-react-native-svg@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754" + integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q== + +"@svgr/babel-plugin-transform-svg-component@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" + integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== + +"@svgr/babel-preset@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece" + integrity sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "8.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "8.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" + "@svgr/babel-plugin-transform-svg-component" "8.0.0" + +"@svgr/core@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88" + integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.1.0" + camelcase "^6.2.0" + cosmiconfig "^8.1.3" + snake-case "^3.0.4" + +"@svgr/hast-util-to-babel-ast@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" + integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q== + dependencies: + "@babel/types" "^7.21.3" + entities "^4.4.0" + +"@svgr/plugin-jsx@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928" + integrity sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.1.0" + "@svgr/hast-util-to-babel-ast" "8.0.0" + svg-parser "^2.0.4" + +"@swc/core-darwin-arm64@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.16.tgz#2cd45d709ce76d448d96bf8d0006849541436611" + integrity sha512-UOCcH1GvjRnnM/LWT6VCGpIk0OhHRq6v1U6QXuPt5wVsgXnXQwnf5k3sG5Cm56hQHDvhRPY6HCsHi/p0oek8oQ== + +"@swc/core-darwin-x64@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.4.16.tgz#a5bc7d8b1dd850adb0bb95c6b5c742b92201fd01" + integrity sha512-t3bgqFoYLWvyVtVL6KkFNCINEoOrIlyggT/kJRgi1y0aXSr0oVgcrQ4ezJpdeahZZ4N+Q6vT3ffM30yIunELNA== + +"@swc/core-linux-arm-gnueabihf@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.16.tgz#961744908ee5cbb79bc009dcf58cc8b831111f38" + integrity sha512-DvHuwvEF86YvSd0lwnzVcjOTZ0jcxewIbsN0vc/0fqm9qBdMMjr9ox6VCam1n3yYeRtj4VFgrjeNFksqbUejdQ== + +"@swc/core-linux-arm64-gnu@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.16.tgz#43713be3f26757d82d2745dc25f8b63400e0a3d0" + integrity sha512-9Uu5YlPbyCvbidjKtYEsPpyZlu16roOZ5c2tP1vHfnU9bgf5Tz5q5VovSduNxPHx+ed2iC1b1URODHvDzbbDuQ== + +"@swc/core-linux-arm64-musl@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.16.tgz#394a7d030f3a61902bd3947bb9d70d26d42f3c81" + integrity sha512-/YZq/qB1CHpeoL0eMzyqK5/tYZn/rzKoCYDviFU4uduSUIJsDJQuQA/skdqUzqbheOXKAd4mnJ1hT04RbJ8FPQ== + +"@swc/core-linux-x64-gnu@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.16.tgz#71eb108b784f9d551ee8a35ebcdaed972f567981" + integrity sha512-UUjaW5VTngZYDcA8yQlrFmqs1tLi1TxbKlnaJwoNhel9zRQ0yG1YEVGrzTvv4YApSuIiDK18t+Ip927bwucuVQ== + +"@swc/core-linux-x64-musl@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.16.tgz#10dbaedb4e3dfc7268e3a9a66ad3431471ef035b" + integrity sha512-aFhxPifevDTwEDKPi4eRYWzC0p/WYJeiFkkpNU5Uc7a7M5iMWPAbPFUbHesdlb9Jfqs5c07oyz86u+/HySBNPQ== + +"@swc/core-win32-arm64-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.16.tgz#80247adff6c245ff32b44d773c1a148858cd655f" + integrity sha512-bTD43MbhIHL2s5QgCwyleaGwl96Gk/scF2TaVKdUe4QlJCDV/YK9h5oIBAp63ckHtE8GHlH4c8dZNBiAXn4Org== + +"@swc/core-win32-ia32-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.16.tgz#e540afc3ccf3224267b4ddfb408f9d9737984686" + integrity sha512-/lmZeAN/qV5XbK2SEvi8e2RkIg8FQNYiSA8y2/Zb4gTUMKVO5JMLH0BSWMiIKMstKDPDSxMWgwJaQHF8UMyPmQ== + +"@swc/core-win32-x64-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.16.tgz#f880939fca32c181adfe7e3abd2b6b7857bd3489" + integrity sha512-BPAfFfODWXtUu6SwaTTftDHvcbDyWBSI/oanUeRbQR5vVWkXoQ3cxLTsDluc3H74IqXS5z1Uyoe0vNo2hB1opA== + +"@swc/core@^1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.4.16.tgz#d175bae2acfecd53bcbd4293f1fba5ec316634a0" + integrity sha512-Xaf+UBvW6JNuV131uvSNyMXHn+bh6LyKN4tbv7tOUFQpXyz/t9YWRE04emtlUW9Y0qrm/GKFCbY8n3z6BpZbTA== + dependencies: + "@swc/counter" "^0.1.2" + "@swc/types" "^0.1.5" + optionalDependencies: + "@swc/core-darwin-arm64" "1.4.16" + "@swc/core-darwin-x64" "1.4.16" + "@swc/core-linux-arm-gnueabihf" "1.4.16" + "@swc/core-linux-arm64-gnu" "1.4.16" + "@swc/core-linux-arm64-musl" "1.4.16" + "@swc/core-linux-x64-gnu" "1.4.16" + "@swc/core-linux-x64-musl" "1.4.16" + "@swc/core-win32-arm64-msvc" "1.4.16" + "@swc/core-win32-ia32-msvc" "1.4.16" + "@swc/core-win32-x64-msvc" "1.4.16" + +"@swc/counter@^0.1.2", "@swc/counter@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== + +"@swc/types@^0.1.5": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.6.tgz#2f13f748995b247d146de2784d3eb7195410faba" + integrity sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg== + dependencies: + "@swc/counter" "^0.1.3" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@thaunknown/thirty-two@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@thaunknown/thirty-two/-/thirty-two-1.0.3.tgz#64883031c4444c0fd75436219189ab5816b5b826" + integrity sha512-bD6PvWbaf53JC04O7WnGDjqZBDgja/KT2Jd/6I2vJBIy+DLmQfQJZZ/G+16nAkVq1yGTIkO4rfc4RlH0DmEEqA== + dependencies: + uint8-util "^2.1.9" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== + dependencies: + "@babel/types" "^7.20.7" + +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/debug@^4.1.6": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/estree@1.0.5", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/fs-extra@9.0.13", "@types/fs-extra@^9.0.11": + version "9.0.13" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" + integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== + dependencies: + "@types/node" "*" + +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +"@types/json-schema@^7.0.12": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3" + integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node@*": + version "20.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^18.11.18", "@types/node@^18.19.9": + version "18.19.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" + integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== + dependencies: + undici-types "~5.26.4" + +"@types/plist@^3.0.1": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.5.tgz#9a0c49c0f9886c8c8696a7904dd703f6284036e0" + integrity sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA== + dependencies: + "@types/node" "*" + xmlbuilder ">=11.0.1" + +"@types/prop-types@*": + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + +"@types/react-dom@^18.2.18": + version "18.2.25" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.25.tgz#2946a30081f53e7c8d585eb138277245caedc521" + integrity sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18.2.48": + version "18.2.79" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.79.tgz#c40efb4f255711f554d47b449f796d1c7756d865" + integrity sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/semver@^7.5.0": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + +"@types/use-sync-external-store@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" + integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== + +"@types/verror@^1.10.3": + version "1.10.10" + resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087" + integrity sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg== + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@^6.14.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.14.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== + dependencies: + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== + +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" + integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== + dependencies: + "@typescript-eslint/types" "6.21.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vanilla-extract/babel-plugin-debug-ids@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.5.tgz#e24424f46dd7737764a4bb5ac6dcdf19240f88bc" + integrity sha512-Rc9A6ylsw7EBErmpgqCMvc/Z/eEZxI5k1xfLQHw7f5HHh3oc5YfzsAsYU/PdmSNjF1dp3sGEViBdDltvwnfVaA== + dependencies: + "@babel/core" "^7.23.9" + +"@vanilla-extract/css@^1.14.2": + version "1.14.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/css/-/css-1.14.2.tgz#121d0b8633ea7e85505e4abab64f60670617d38d" + integrity sha512-OasEW4ojGqqRiUpsyEDUMrSkLnmwbChtafkogpCZ1eDAgAZ9eY9CHLYodj2nB8aV5T25kQ5shm92k25ngjYhhg== + dependencies: + "@emotion/hash" "^0.9.0" + "@vanilla-extract/private" "^1.0.4" + chalk "^4.1.1" + css-what "^6.1.0" + cssesc "^3.0.0" + csstype "^3.0.7" + deep-object-diff "^1.1.9" + deepmerge "^4.2.2" + media-query-parser "^2.0.2" + modern-ahocorasick "^1.0.0" + outdent "^0.8.0" + +"@vanilla-extract/integration@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/integration/-/integration-7.1.2.tgz#1685cc899babb3d6f31f4450d95a8324acc9ed17" + integrity sha512-jpjw0L3P1E+U9L8OAFVMGpPFbNPD+/Vpfew7oOKBYipCrRZEqShu3WLXuUxjXz/mcIH7KCS5nasIdy2VclbEaQ== + dependencies: + "@babel/core" "^7.23.9" + "@babel/plugin-syntax-typescript" "^7.23.3" + "@vanilla-extract/babel-plugin-debug-ids" "^1.0.5" + "@vanilla-extract/css" "^1.14.2" + esbuild "npm:esbuild@~0.17.6 || ~0.18.0 || ~0.19.0" + eval "0.1.8" + find-up "^5.0.0" + javascript-stringify "^2.0.1" + lodash "^4.17.21" + mlly "^1.4.2" + outdent "^0.8.0" + vite "^5.0.11" + vite-node "^1.2.0" + +"@vanilla-extract/private@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@vanilla-extract/private/-/private-1.0.4.tgz#35946b917d6b9774a2b9bc725c63c9341049c79b" + integrity sha512-8FGD6AejeC/nXcblgNCM5rnZb9KXa4WNkR03HCWtdJBpANjTgjHEglNLFnhuvdQ78tC6afaxBPI+g7F2NX3tgg== + +"@vanilla-extract/recipes@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/recipes/-/recipes-0.5.2.tgz#fccd09af9ce1ab3abd17fcf20cd649df0e7f30d3" + integrity sha512-IcDw3tFOcSJ+DUxL8MIGbg03eyccYb6NBO/rcgp439PDuHxo5GIQefVeGc1L5mIr/lUVPwDc1N5OXTHiGpiz1A== + +"@vanilla-extract/vite-plugin@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@vanilla-extract/vite-plugin/-/vite-plugin-4.0.7.tgz#40cd5de73bacf89e2ca69ba61163f50e62e77b3c" + integrity sha512-uRAFWdq5Eq0ybpgW2P0LNOw8eW+MTg/OFo5K0Hsl2cKu/Tu2tabCsBf6vNjfhDrm4jBShHy1wJIVcYxf6sczVQ== + dependencies: + "@vanilla-extract/integration" "^7.1.2" + +"@vitejs/plugin-react@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz#744d8e4fcb120fc3dbaa471dadd3483f5a304bb9" + integrity sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ== + dependencies: + "@babel/core" "^7.23.5" + "@babel/plugin-transform-react-jsx-self" "^7.23.3" + "@babel/plugin-transform-react-jsx-source" "^7.23.3" + "@types/babel__core" "^7.20.5" + react-refresh "^0.14.0" + +"@xmldom/xmldom@^0.8.8": + version "0.8.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.11.3, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + +ajv-keywords@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +app-builder-bin@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-4.0.0.tgz#1df8e654bd1395e4a319d82545c98667d7eed2f0" + integrity sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA== + +app-builder-lib@24.13.3: + version "24.13.3" + resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-24.13.3.tgz#36e47b65fecb8780bb73bff0fee4e0480c28274b" + integrity sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig== + dependencies: + "@develar/schema-utils" "~2.6.5" + "@electron/notarize" "2.2.1" + "@electron/osx-sign" "1.0.5" + "@electron/universal" "1.5.1" + "@malept/flatpak-bundler" "^0.4.0" + "@types/fs-extra" "9.0.13" + async-exit-hook "^2.0.1" + bluebird-lst "^1.0.9" + builder-util "24.13.1" + builder-util-runtime "9.2.4" + chromium-pickle-js "^0.2.0" + debug "^4.3.4" + ejs "^3.1.8" + electron-publish "24.13.1" + form-data "^4.0.0" + fs-extra "^10.1.0" + hosted-git-info "^4.1.0" + is-ci "^3.0.0" + isbinaryfile "^5.0.0" + js-yaml "^4.1.0" + lazy-val "^1.0.5" + minimatch "^5.1.1" + read-config-file "6.3.2" + sanitize-filename "^1.6.3" + semver "^7.3.8" + tar "^6.1.12" + temp-file "^3.4.0" + +app-root-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86" + integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlast@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + 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" + +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.toreversed@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" + integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" + integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.1.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-exit-hook@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" + integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== + +async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + +base64-js@^1.3.1, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bencode@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/bencode/-/bencode-4.0.0.tgz#36ca0bc366290dad002215fc52fc74edf4eb0625" + integrity sha512-AERXw18df0pF3ziGOCyUjqKZBVNH8HV3lBxnx5w0qtgMIk4a1wb9BkcCQbkp9Zstfrn/dzRwl7MmUHHocX3sRQ== + dependencies: + uint8-util "^2.2.2" + +bep53-range@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bep53-range/-/bep53-range-2.0.0.tgz#a1770475661b4b814c4359e4b66f7cbd88de2b10" + integrity sha512-sMm2sV5PRs0YOVk0LTKtjuIprVzxgTQUsrGX/7Yph2Rm4FO2Fqqtq7hNjsOB5xezM4v4+5rljCgK++UeQJZguA== + +better-sqlite3@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.5.0.tgz#0e10766cfb7f9b8916be3ab95ad9d5bcc4e6e6fd" + integrity sha512-01qVcM4gPNwE+PX7ARNiHINwzVuD6nx0gdldaAAcu+MrzyIAukQ31ZDKEpzRO/CNA9sHpxoTZ8rdjoyAin4dyg== + dependencies: + bindings "^1.5.0" + prebuild-install "^7.1.1" + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bluebird-lst@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c" + integrity sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw== + dependencies: + bluebird "^3.5.5" + +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +boolean@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.22.2: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-equal@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.1.tgz#2f7651be5b1b3f057fcd6e7ee16cf34767077d90" + integrity sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.1.0, buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +builder-util-runtime@9.2.4: + version "9.2.4" + resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz#13cd1763da621e53458739a1e63f7fcba673c42a" + integrity sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA== + dependencies: + debug "^4.3.4" + sax "^1.2.4" + +builder-util@24.13.1: + version "24.13.1" + resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-24.13.1.tgz#4a4c4f9466b016b85c6990a0ea15aa14edec6816" + integrity sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA== + dependencies: + "7zip-bin" "~5.2.0" + "@types/debug" "^4.1.6" + app-builder-bin "4.0.0" + bluebird-lst "^1.0.9" + builder-util-runtime "9.2.4" + chalk "^4.1.2" + cross-spawn "^7.0.3" + debug "^4.3.4" + fs-extra "^10.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-ci "^3.0.0" + js-yaml "^4.1.0" + source-map-support "^0.5.19" + stat-mode "^1.0.0" + temp-file "^3.4.0" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + 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" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001587: + version "1.0.30001611" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz#4dbe78935b65851c2d2df1868af39f709a93a96e" + integrity sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.1, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-disk-space@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/check-disk-space/-/check-disk-space-3.4.0.tgz#eb8e69eee7a378fd12e35281b8123a8b4c4a8ff7" + integrity sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw== + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chromium-pickle-js@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" + integrity sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + +cli-highlight@^2.1.11: + version "2.1.11" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" + integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg== + dependencies: + chalk "^4.0.0" + highlight.js "^10.7.1" + mz "^2.4.0" + parse5 "^5.1.1" + parse5-htmlparser2-tree-adapter "^6.0.0" + yargs "^16.0.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color.js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/color.js/-/color.js-1.2.0.tgz#18d9f55545111730d25ccf18ea8b6933c71440d7" + integrity sha512-0ajlNgWWOR7EK9N6l2h0YKsZPzMCLQG5bheCoTGpGfhkR8tB5eQNItdua1oFHDTeq9JKgSzQJqo+Gp3V/xW+Lw== + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +compare-version@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" + integrity sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" + integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== + +config-file-ts@^0.2.4: + version "0.2.6" + resolved "https://registry.yarnpkg.com/config-file-ts/-/config-file-ts-0.2.6.tgz#b424ff74612fb37f626d6528f08f92ddf5d22027" + integrity sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w== + dependencies: + glob "^10.3.10" + typescript "^5.3.3" + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +cosmiconfig@^8.1.3: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +crc@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" + +cross-fetch-ponyfill@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cross-fetch-ponyfill/-/cross-fetch-ponyfill-1.0.3.tgz#5c5524e3bd3374e71d5016c2327e416369a57527" + integrity sha512-uOBkDhUAGAbx/FEzNKkOfx3w57H8xReBBXoZvUnOKTI0FW0Xvrj3GrYv2iZXUqlffC1LMGfQzhmBM/ke+6eTDA== + dependencies: + abort-controller "^3.0.0" + node-fetch "^3.3.0" + +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssstyle@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.0.1.tgz#ef29c598a1e90125c870525490ea4f354db0660a" + integrity sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ== + dependencies: + rrweb-cssom "^0.6.0" + +csstype@^3.0.2, csstype@^3.0.7: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +csv-generate@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" + integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== + +csv-parse@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" + integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== + +csv-stringify@^5.6.5: + version "5.6.5" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00" + integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== + +csv@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d" + integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== + dependencies: + csv-generate "^3.4.3" + csv-parse "^4.16.3" + csv-stringify "^5.6.5" + stream-transform "^2.1.3" + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + +dayjs@^1.11.9: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deep-object-diff@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.9.tgz#6df7ef035ad6a0caa44479c536ed7b02570f4595" + integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA== + +deepmerge@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-libc@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +dir-compare@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-3.3.0.tgz#2c749f973b5c4b5d087f11edaae730db31788416" + integrity sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg== + dependencies: + buffer-equal "^1.0.0" + minimatch "^3.0.4" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dmg-builder@24.13.3: + version "24.13.3" + resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-24.13.3.tgz#95d5b99c587c592f90d168a616d7ec55907c7e55" + integrity sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ== + dependencies: + app-builder-lib "24.13.3" + builder-util "24.13.1" + builder-util-runtime "9.2.4" + fs-extra "^10.1.0" + iconv-lite "^0.6.2" + js-yaml "^4.1.0" + optionalDependencies: + dmg-license "^1.0.11" + +dmg-license@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/dmg-license/-/dmg-license-1.0.11.tgz#7b3bc3745d1b52be7506b4ee80cb61df6e4cd79a" + integrity sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q== + 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" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^16.0.3: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +dotenv@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-9.0.2.tgz#dacc20160935a37dea6364aa1bef819fb9b6ab05" + integrity sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ejs@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +electron-builder@^24.9.1: + version "24.13.3" + resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-24.13.3.tgz#c506dfebd36d9a50a83ee8aa32d803d83dbe4616" + integrity sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg== + dependencies: + app-builder-lib "24.13.3" + builder-util "24.13.1" + builder-util-runtime "9.2.4" + chalk "^4.1.2" + dmg-builder "24.13.3" + fs-extra "^10.1.0" + is-ci "^3.0.0" + lazy-val "^1.0.5" + read-config-file "6.3.2" + simple-update-notifier "2.0.0" + yargs "^17.6.2" + +electron-publish@24.13.1: + version "24.13.1" + resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-24.13.1.tgz#57289b2f7af18737dc2ad134668cdd4a1b574a0c" + integrity sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A== + dependencies: + "@types/fs-extra" "^9.0.11" + builder-util "24.13.1" + builder-util-runtime "9.2.4" + chalk "^4.1.2" + fs-extra "^10.1.0" + lazy-val "^1.0.5" + mime "^2.5.2" + +electron-to-chromium@^1.4.668: + version "1.4.745" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.745.tgz#9c202ce9cbf18a5b5e0ca47145fd127cc4dd2290" + integrity sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA== + +electron-vite@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/electron-vite/-/electron-vite-2.1.0.tgz#33908c3b9c90bcab5c5f4c0f6c483263303cc5aa" + integrity sha512-DjToUW6q3ILoW79b1yBywC6LubnOw5Axr2zo9cHMlYf00zAO8oVzrCcqinJQTTbJLvqCuVcBzuICMl5MYshUnQ== + dependencies: + "@babel/core" "^7.23.5" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + cac "^6.7.14" + esbuild "^0.19.8" + magic-string "^0.30.5" + picocolors "^1.0.0" + +electron@^28.2.0: + version "28.3.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-28.3.1.tgz#babb3ff8e246336e9cd1c1966f16a55ba723ea06" + integrity sha512-aF9fONuhVDJlctJS7YOw76ynxVAQdfIWmlhRMKits24tDcdSL0eMHUS0wWYiRfGWbQnUKB6V49Rf17o32f4/fg== + dependencies: + "@electron/get" "^2.0.0" + "@types/node" "^18.11.18" + extract-zip "^2.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-iterator-helpers@^1.0.17: + version "1.0.18" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz#4d3424f46b24df38d064af6fbbc89274e29ea69d" + integrity sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.2" + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +esbuild@^0.19.8, "esbuild@npm:esbuild@~0.17.6 || ~0.18.0 || ~0.19.0": + version "0.19.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" + integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.12" + "@esbuild/android-arm" "0.19.12" + "@esbuild/android-arm64" "0.19.12" + "@esbuild/android-x64" "0.19.12" + "@esbuild/darwin-arm64" "0.19.12" + "@esbuild/darwin-x64" "0.19.12" + "@esbuild/freebsd-arm64" "0.19.12" + "@esbuild/freebsd-x64" "0.19.12" + "@esbuild/linux-arm" "0.19.12" + "@esbuild/linux-arm64" "0.19.12" + "@esbuild/linux-ia32" "0.19.12" + "@esbuild/linux-loong64" "0.19.12" + "@esbuild/linux-mips64el" "0.19.12" + "@esbuild/linux-ppc64" "0.19.12" + "@esbuild/linux-riscv64" "0.19.12" + "@esbuild/linux-s390x" "0.19.12" + "@esbuild/linux-x64" "0.19.12" + "@esbuild/netbsd-x64" "0.19.12" + "@esbuild/openbsd-x64" "0.19.12" + "@esbuild/sunos-x64" "0.19.12" + "@esbuild/win32-arm64" "0.19.12" + "@esbuild/win32-ia32" "0.19.12" + "@esbuild/win32-x64" "0.19.12" + +esbuild@^0.20.1: + version "0.20.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" + integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== + optionalDependencies: + "@esbuild/aix-ppc64" "0.20.2" + "@esbuild/android-arm" "0.20.2" + "@esbuild/android-arm64" "0.20.2" + "@esbuild/android-x64" "0.20.2" + "@esbuild/darwin-arm64" "0.20.2" + "@esbuild/darwin-x64" "0.20.2" + "@esbuild/freebsd-arm64" "0.20.2" + "@esbuild/freebsd-x64" "0.20.2" + "@esbuild/linux-arm" "0.20.2" + "@esbuild/linux-arm64" "0.20.2" + "@esbuild/linux-ia32" "0.20.2" + "@esbuild/linux-loong64" "0.20.2" + "@esbuild/linux-mips64el" "0.20.2" + "@esbuild/linux-ppc64" "0.20.2" + "@esbuild/linux-riscv64" "0.20.2" + "@esbuild/linux-s390x" "0.20.2" + "@esbuild/linux-x64" "0.20.2" + "@esbuild/netbsd-x64" "0.20.2" + "@esbuild/openbsd-x64" "0.20.2" + "@esbuild/sunos-x64" "0.20.2" + "@esbuild/win32-arm64" "0.20.2" + "@esbuild/win32-ia32" "0.20.2" + "@esbuild/win32-x64" "0.20.2" + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + +eslint-plugin-prettier@^5.0.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz#17cfade9e732cef32b5f5be53bd4e07afd8e67e1" + integrity sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.8.6" + +eslint-plugin-react@^7.33.2: + version "7.34.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz#6806b70c97796f5bbfb235a5d3379ece5f4da997" + integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlast "^1.2.4" + array.prototype.flatmap "^1.3.2" + array.prototype.toreversed "^1.1.2" + array.prototype.tosorted "^1.1.3" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.17" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.7" + object.fromentries "^2.0.7" + object.hasown "^1.1.3" + object.values "^1.1.7" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.10" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.56.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eval@0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.8.tgz#2b903473b8cc1d1989b83a1e7923f883eb357f85" + integrity sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw== + dependencies: + "@types/node" "*" + require-like ">= 0.1.1" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +extract-zip@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +flexsearch@^0.7.43: + version "0.7.43" + resolved "https://registry.yarnpkg.com/flexsearch/-/flexsearch-0.7.43.tgz#34f89b36278a466ce379c5bf6fb341965ed3f16c" + integrity sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg== + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^10.0.0, fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.0.0, fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stdin@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" + integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.3.10: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + +glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +global-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" + integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== + 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" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.1, globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + 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" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +highlight.js@^10.7.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + +i18next-browser-languagedetector@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz#1968196d437b4c8db847410c7c33554f6c448f6f" + integrity sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw== + dependencies: + "@babel/runtime" "^7.23.2" + +i18next@^23.11.2: + version "23.11.2" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.11.2.tgz#4c0e8192a9ba230fe7dc68b76459816ab601826e" + integrity sha512-qMBm7+qT8jdpmmDw/kQD16VpmkL9BdL+XNAK5MNbNFaf1iQQq35ZbPrSlqmnNPOSUY4m342+c0t0evinF5l7sA== + dependencies: + "@babel/runtime" "^7.23.2" + +iconv-corefoundation@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz#31065e6ab2c9272154c8b0821151e2c88f1b002a" + integrity sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ== + dependencies: + cli-truncate "^2.1.0" + node-addon-api "^1.6.3" + +iconv-lite@0.6.3, iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +immer@^10.0.3: + version "10.0.4" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.4.tgz#09af41477236b99449f9d705369a4daaf780362b" + integrity sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw== + +import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-ci@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isbinaryfile@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.2.tgz#fe6e4dfe2e34e947ffa240c113444876ba393ae0" + integrity sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +javascript-stringify@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" + integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdom@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.0.0.tgz#e2dc04e4c79da368481659818ee2b0cd7c39007c" + integrity sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A== + dependencies: + cssstyle "^4.0.1" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.7" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.3" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.16.0" + xml-name-validator "^5.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^2.2.0, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.0.0, keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +lazy-val@^1.0.4, lazy-val@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.5.tgz#6cf3b9f5bc31cee7ee3e369c0832b7583dcd923d" + integrity sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lottie-react@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/lottie-react/-/lottie-react-2.4.0.tgz#f7249eee2b1deee70457a2d142194fdf2456e4bd" + integrity sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w== + dependencies: + lottie-web "^5.10.2" + +lottie-web@^5.10.2: + version "5.12.2" + resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.12.2.tgz#579ca9fe6d3fd9e352571edd3c0be162492f68e5" + integrity sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg== + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.30.5: + version "0.30.10" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" + integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +magnet-uri@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/magnet-uri/-/magnet-uri-7.0.5.tgz#7b5143fd5527f3f612959eeeae264d6f4aeff37b" + integrity sha512-Ke+dDiYHK1Rq/ZyGUAgk7NIkoypivxolTj/A0qr60ypP0FjeP+NTUNEhr965HsRan0zGxKEBK73+SsjRyJWkXg== + dependencies: + "@thaunknown/thirty-two" "^1.0.3" + bep53-range "^2.0.0" + uint8-util "^2.1.9" + +matcher@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== + dependencies: + escape-string-regexp "^4.0.0" + +media-query-parser@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/media-query-parser/-/media-query-parser-2.0.2.tgz#ff79e56cee92615a304a1c2fa4f2bd056c0a1d29" + integrity sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w== + dependencies: + "@babel/runtime" "^7.12.5" + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mixme@^0.5.1: + version "0.5.10" + resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.10.tgz#d653b2984b75d9018828f1ea333e51717ead5f51" + integrity sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^2.1.3: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + +mlly@^1.4.2, mlly@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.6.1.tgz#0983067dc3366d6314fc5e12712884e6978d028f" + integrity sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA== + dependencies: + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.0.3" + ufo "^1.3.2" + +modern-ahocorasick@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/modern-ahocorasick/-/modern-ahocorasick-1.0.1.tgz#dec373444f51b5458ac05216a8ec376e126dd283" + integrity sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mz@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-abi@^3.3.0: + version "3.60.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.60.0.tgz#a325b13b3c401c2230202897559fbf0b5f9a90ac" + integrity sha512-zcGgwoXbzw9NczqbGzAWL/ToDYAxv1V8gL1D67ClbdkIfeeDBbY0GelZtC25ayLvVjr2q2cloHeQV1R0QAWqRQ== + dependencies: + semver "^7.3.5" + +node-addon-api@^1.6.3: + version "1.7.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" + integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +nwsapi@^2.2.7: + version "2.2.8" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.8.tgz#a3552e65b74bf8cc89d0480c4132b61dbe54eccf" + integrity sha512-GU/I3lTEFQ9mkEm07Q7HvdRajss8E1wVMGOk3/lHl60QPseG+B3BIQY+JUjYWw7gF8cCeoQCXd4N7DB7avw0Rg== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.7: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.hasown@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" + integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== + dependencies: + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +outdent@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.8.0.tgz#2ebc3e77bf49912543f1008100ff8e7f44428eb0" + integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A== + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-torrent@^11.0.16: + version "11.0.16" + resolved "https://registry.yarnpkg.com/parse-torrent/-/parse-torrent-11.0.16.tgz#839abce12c3c376f08516b8608a551154963b99c" + integrity sha512-5GoOdmW0HpiB78aQpBz8/5V3V1LjBRDNiL7DOs33pKeCLOzFnfMrsRD6CYmaUBT5Vi/dXE0hfePsjDGJSMF48w== + dependencies: + bencode "^4.0.0" + cross-fetch-ponyfill "^1.0.3" + get-stdin "^9.0.0" + magnet-uri "^7.0.5" + queue-microtask "^1.2.3" + uint8-util "^2.2.4" + +parse5-htmlparser2-tree-adapter@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^1.1.1, pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-types@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.0.tgz#3ec1bf33379030fd0a34c227b6c650e8ea7ca271" + integrity sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA== + dependencies: + confbox "^0.1.7" + mlly "^1.6.1" + pathe "^1.1.2" + +plist@^3.0.4, plist@^3.0.5: + version "3.1.0" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" + integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss@^8.4.38: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + +prebuild-install@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^3.2.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +ps-list@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/ps-list/-/ps-list-8.1.1.tgz#9ff1952b26a9a07fcc05270407e60544237ae581" + integrity sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ== + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2, queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-i18next@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.0.tgz#44da74fbffd416f5d0c5307ef31735cf10cc91d9" + integrity sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ== + dependencies: + "@babel/runtime" "^7.23.9" + html-parse-stringify "^3.0.1" + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-loading-skeleton@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-loading-skeleton/-/react-loading-skeleton-3.4.0.tgz#c71a3a17259d08e4064974aa0b07f150a09dfd57" + integrity sha512-1oJEBc9+wn7BbkQQk7YodlYEIjgeR+GrRjD+QXkVjwZN7LGIcAFHrx4NhT7UHGBxNY1+zax3c+Fo6XQM4R7CgA== + +react-redux@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.1.tgz#852ec13084bd7375e26db697d2fc9027ffada204" + integrity sha512-5ynfGDzxxsoV73+4czQM56qF43vsmgJsO22rmAvU5tZT2z5Xow/A2uhhxwXuGTxgdReF3zcp7A80gma2onRs1A== + dependencies: + "@types/use-sync-external-store" "^0.0.3" + use-sync-external-store "^1.0.0" + +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + +react-router-dom@^6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.3.tgz#9781415667fd1361a475146c5826d9f16752a691" + integrity sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw== + dependencies: + "@remix-run/router" "1.15.3" + react-router "6.22.3" + +react-router@6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.3.tgz#9d9142f35e08be08c736a2082db5f0c9540a885e" + integrity sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ== + dependencies: + "@remix-run/router" "1.15.3" + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-config-file@6.3.2: + version "6.3.2" + resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-6.3.2.tgz#556891aa6ffabced916ed57457cb192e61880411" + integrity sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q== + dependencies: + config-file-ts "^0.2.4" + dotenv "^9.0.2" + dotenv-expand "^5.1.0" + js-yaml "^4.1.0" + json5 "^2.2.0" + lazy-val "^1.0.4" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + +reflect-metadata@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + +reflect.getprototypeof@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +"require-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +reselect@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.0.tgz#c479139ab9dd91be4d9c764a7f3868210ef8cd21" + integrity sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg== + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +roarr@^2.15.3: + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== + 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" + +rollup@^4.13.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.16.0.tgz#6234ae756ff14bce900c1c926175d14cc3e1f5f4" + integrity sha512-joxy/Hd4Ee289394//Q1aoebcxXyHasDieCTk8YtP4G4al4TUlx85EnuCLrfrdtLzrna9kNjH++Sx063wxSgmA== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.16.0" + "@rollup/rollup-android-arm64" "4.16.0" + "@rollup/rollup-darwin-arm64" "4.16.0" + "@rollup/rollup-darwin-x64" "4.16.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.16.0" + "@rollup/rollup-linux-arm-musleabihf" "4.16.0" + "@rollup/rollup-linux-arm64-gnu" "4.16.0" + "@rollup/rollup-linux-arm64-musl" "4.16.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.16.0" + "@rollup/rollup-linux-riscv64-gnu" "4.16.0" + "@rollup/rollup-linux-s390x-gnu" "4.16.0" + "@rollup/rollup-linux-x64-gnu" "4.16.0" + "@rollup/rollup-linux-x64-musl" "4.16.0" + "@rollup/rollup-win32-arm64-msvc" "4.16.0" + "@rollup/rollup-win32-ia32-msvc" "4.16.0" + "@rollup/rollup-win32-x64-msvc" "4.16.0" + fsevents "~2.3.2" + +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sanitize-filename@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +sec@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sec/-/sec-2.0.0.tgz#4f5b82e7d1da27da6c7a40799e63caef21ce374f" + integrity sha512-uq35HWa7mG6YyojrduMXjF8UhOySEf3X0V1uMpSOBYUF09xAMnJaKNSmWXeE3mN7NfJTpNUkmGa6nIpEBMN8Xw== + +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== + +semver@^6.2.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + 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" + +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +sha.js@^2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +simple-update-notifier@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +smart-buffer@^4.0.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-support@^0.5.19: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + +stat-mode@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465" + integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== + +stream-transform@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3" + integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== + dependencies: + mixme "^0.5.1" + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.10: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + 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" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +sumchecker@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" + integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== + dependencies: + debug "^4.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +synckit@^0.8.6: + version "0.8.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" + integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^6.1.12: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tasklist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tasklist/-/tasklist-5.0.0.tgz#0214f8a28c0fa9e042333154e3e3faa45c640404" + integrity sha512-qPB4J6pseXRqdxAFT1GhlvDPv4FHxWkXs8QVYQWIqusGwn7UXVKOoYu09DZuYWe1K7T5iusHfSoKrv8k9+RfxA== + dependencies: + csv "^5.5.0" + sec "^2.0.0" + +temp-file@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.4.0.tgz#766ea28911c683996c248ef1a20eea04d51652c7" + integrity sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg== + dependencies: + async-exit-hook "^2.0.1" + fs-extra "^10.0.0" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tmp-promise@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" + integrity sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== + dependencies: + tmp "^0.2.0" + +tmp@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== + dependencies: + punycode "^2.3.1" + +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== + dependencies: + utf8-byte-length "^1.0.1" + +ts-api-utils@^1.0.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + +tslib@^2.0.3, tslib@^2.5.0, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typeorm@^0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.20.tgz#4b61d737c6fed4e9f63006f88d58a5e54816b7ab" + integrity sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q== + dependencies: + "@sqltools/formatter" "^1.2.5" + app-root-path "^3.1.0" + buffer "^6.0.3" + chalk "^4.1.2" + cli-highlight "^2.1.11" + dayjs "^1.11.9" + debug "^4.3.4" + dotenv "^16.0.3" + glob "^10.3.10" + mkdirp "^2.1.3" + reflect-metadata "^0.2.1" + sha.js "^2.4.11" + tslib "^2.5.0" + uuid "^9.0.0" + yargs "^17.6.2" + +typescript@^5.3.3: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +ufo@^1.3.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344" + integrity sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw== + +uint8-util@^2.1.9, uint8-util@^2.2.2, uint8-util@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/uint8-util/-/uint8-util-2.2.4.tgz#4df57c7b25b821d02b1599e80f0f1f5f56bc90e1" + integrity sha512-uEI5lLozmKQPYEevfEhP9LY3Je5ZmrQhaWXrzTVqrLNQl36xsRh8NiAxYwB9J+2BAt99TRbmCkROQB2ZKhx4UA== + dependencies: + base64-arraybuffer "^1.0.2" + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +use-sync-external-store@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +utf8-byte-length@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" + integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +verror@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" + integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vite-node@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.5.0.tgz#7f74dadfecb15bca016c5ce5ef85e5cc4b82abf2" + integrity sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^5.0.0" + +vite-plugin-svgr@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz#9f3bf5206b0ec510287e56d16f1915e729bb4e6b" + integrity sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA== + dependencies: + "@rollup/pluginutils" "^5.0.5" + "@svgr/core" "^8.1.0" + "@svgr/plugin-jsx" "^8.1.0" + +vite@^5.0.0, vite@^5.0.11, vite@^5.0.12: + version "5.2.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.10.tgz#2ac927c91e99d51b376a5c73c0e4b059705f5bd7" + integrity sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw== + dependencies: + esbuild "^0.20.1" + postcss "^8.4.38" + rollup "^4.13.0" + optionalDependencies: + fsevents "~2.3.3" + +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +whatwg-url@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" + integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== + dependencies: + tr46 "^5.0.0" + webidl-conversions "^7.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + +xmlbuilder@>=11.0.1, xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^16.0.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + 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" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==