From 085da6b99ee90e8d692ea30e3329c6643e213828 Mon Sep 17 00:00:00 2001 From: Ivan Savoskin Date: Tue, 6 Aug 2024 20:54:44 +0300 Subject: [PATCH 1/2] Notification when a fee leaves a specified border (#9) Features: Add the ability to enable notification when a fee leaves a specified border; Add full localisation to Russian Other changes; Refactoring for messaging; Update deps; --- .github/workflows/release.yml | 78 + .husky/pre-commit | 1 + README.RU.md | 101 ++ README.md | 36 +- package-lock.json | 1400 ++++++++++------- package.json | 77 +- privacy-policy.md | 7 + public/_locales/en/messages.json | 107 ++ public/_locales/ru/messages.json | 107 ++ src/background/index.ts | 113 +- src/background/utils/releaseNotes.ts | 16 +- src/models/block/types.ts | 4 + src/models/fee/types.ts | 18 + src/models/messages/enums.ts | 31 + src/models/messages/types.ts | 115 ++ src/models/types.ts | 126 -- src/offScreen/index.ts | 16 +- .../BlockNotificationRadioButtonGroup.tsx | 7 +- .../Main/ChangeBlockchainRadioButtonGroup.tsx | 16 +- src/popup/components/Main/FeeInfo.tsx | 37 +- .../Main/FeeNotificationRadioButtonGroup.tsx | 7 +- src/popup/components/Main/LastBlockInfo.tsx | 48 +- src/popup/components/Main/index.tsx | 3 +- .../Main/styles/FeeInfo.module.scss | 4 +- .../Main/styles/LastBlockInfo.module.scss | 2 +- .../Main/styles/RadioButtons.module.scss | 2 +- .../BlockNotificationTab/index.tsx | 11 +- .../FeeNotificationTab/FeeBorderCheckbox.tsx | 47 + .../FeeNotificationTab/FeeBorderInput.tsx | 21 +- .../SettingsTabs/FeeNotificationTab/index.tsx | 13 +- .../styles/FeeBorderCheckbox.module.scss | 8 + .../styles/FeeBorderInput.module.scss | 2 +- .../Settings/SettingsTabs/SoundSelector.tsx | 14 +- .../Settings/SettingsTabs/VolumeControl.tsx | 11 +- .../Settings/SettingsTabs/index.tsx | 7 +- .../styles/SoundSelector.module.scss | 2 +- .../styles/VolumeControl.module.scss | 2 +- src/popup/context/WithMainContext.tsx | 16 +- src/popup/styles/Popup.module.scss | 2 +- src/utils/localeUtils.ts | 1 + src/utils/{utils.ts => messagesUtils.ts} | 6 +- webpack.config.ts | 5 + 42 files changed, 1802 insertions(+), 845 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .husky/pre-commit create mode 100644 README.RU.md create mode 100644 privacy-policy.md create mode 100644 public/_locales/en/messages.json create mode 100644 public/_locales/ru/messages.json create mode 100644 src/models/block/types.ts create mode 100644 src/models/fee/types.ts create mode 100644 src/models/messages/enums.ts create mode 100644 src/models/messages/types.ts delete mode 100644 src/models/types.ts create mode 100644 src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderCheckbox.tsx create mode 100644 src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderCheckbox.module.scss create mode 100644 src/utils/localeUtils.ts rename src/utils/{utils.ts => messagesUtils.ts} (73%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..eaea882 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,78 @@ +env: + DIRECTORY: dist + +name: Release +on: + workflow_dispatch: + inputs: + version: + description: 'Release version. Example: 1.0.0' + required: true + type: string +jobs: + Version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 20 + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: npm + - name: install + run: npm ci || npm install + - name: Create tag + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/v${{ inputs.version }}>', + sha: context.sha + }) + - if: ${{ inputs.version }} + name: Create release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: > + await github.request(`POST /repos/${{ github.repository + }}/releases`, { + tag_name: "v${{ inputs.version }}", + generate_release_notes: true + }); + Submit: + if: github.event_name == 'workflow_dispatch' + strategy: + fail-fast: false + matrix: + command: + - chrome + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: npm + - name: install + run: npm ci || npm install + - run: npm run prod --if-present + - name: Update extension’s meta + run: >- + npx dot-json@1 $DIRECTORY/manifest.json version ${{ inputs.version }} + - name: Submit + run: | + case ${{ matrix.command }} in + chrome) + cd $DIRECTORY && npx chrome-webstore-upload-cli@2 upload --auto-publish + ;; + esac + env: + EXTENSION_ID: ${{ secrets.EXTENSION_ID }} + CLIENT_ID: ${{ secrets.CLIENT_ID }} + CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }} + REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..d0a7784 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged \ No newline at end of file diff --git a/README.RU.md b/README.RU.md new file mode 100644 index 0000000..c72eaab --- /dev/null +++ b/README.RU.md @@ -0,0 +1,101 @@ +
+ +
+

Bitcoin blocks tracker

+ +

Расширение для отслеживания блоков Bitcoin и комиссий с помощью Mempool Space

+ +[README in English](./README) + + + Chrome Web Store + + + Новейший релиз + + + GitHub основной язык + + + Сборка + +
+ +
+ +## Введение + +Это расширение создано для того, чтобы легко и удобно отслеживать появление новых блоков в сети Bitcoin. +После того, как новый блок будет смайнен, расширение оповещает пользователя звуковым сигналом. +Расширение также позволяет быстро отслеживать текущие комиссии в сети. + +Получение данных о блоках и комиссиях осуществляется с помощью [mempool.space](https://mempool.space/) + +## Функции + +- Получение звукового уведомления при появлении нового блока в основной или тестовой сети +сети Bitcoin +- Получение звукового уведомления при пересечении комиссией в сети заданного значения +- Получение информации о текущих комиссиях в сети Bitcoin +- Получение информации о времени появления последнего блока + +## Установка + +**Нажмите [сюда][1], а затем нажмите "Установить" на открывшейся странице** + +> * Разработано и протестировано для **Google Chrome** +> * Может быть установлено в любом браузере Chromium - Opera (GX), Vivaldi и т.д. +> * В Microsoft Edge сначала нажмите «Разрешить расширения из других магазинов» (появится запрос) + +### Когда версия для Firefox? + +На данный момент **версия для Firefox не планируется**. + +1. Firefox не поддерживает некоторые важные функции расширения *(PiP API и background service workers)*. +2. Для частичной поддержки потребуется сильно изменить рабочий процесс. +3. Нет спроса на версию для Firefox. + +## Локальная установка + +### Предустановки +1. Скачай [свежий релиз][2] либо весь репозиторий +2. Установите Node.js (требуемая версия в [package.json](./package.json)) +3. Должен быть установлен совместимый `npm` +4. В терминале выполните команду `npm install` из папки проекта + +### Линтеры +Для контроля качества кода предусмотрено подключение линтеров. + +#### ESLint +Правила для ESLint указаны в файле `/.eslintrc`. + +Проверка кода с использованием ESLint запускается командой `npm run eslint`. + +#### Stylelint +Правила для Stylelint указаны в файле `/.stylelintrc.json`. + +Проверка стилей с помощью Stylelint запускается командой + +### Сборка для разработки +Запустите dev сборку с помощью команды `npm run dev`. + +После сборки будет создана папка `/dist` со сборкой расширения, +которую можно использовать для добавления в браузер. + +Каждое изменение кода автоматически инициирует пересборку расширения. + +Во время сборки также добавляется source map, позволяющие использовать Chrome Dev Tools + +### Промышленная сборка +Запустите prod сборку с помощью команды `npm run prod`. + +Перед сборкой автоматически запускается проверка кода с помощью ESLint и StyleLint. + +Собранный проект сохраняется в папке `/dist`. + +### Загрузить расширение в Chrome + +Загрузить каталог `dist` на странице расширения Chrome ([инструкция](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked)) + +[1]: https://chromewebstore.google.com/detail/bitcoin-blocks-tracker/jhdbfjhembciojemihcimllmbibiakim +[2]: https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases diff --git a/README.md b/README.md index aa7763c..9d21261 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,13 @@

Extension for track Bitcoin blocks and fees via Mempool space

+[README на Русском](./README.RU) + - Chrome Web Store + Chrome Web Store + + + Newest release GitHub top language @@ -30,15 +35,33 @@ Obtaining blocks and fees data is done using [mempool.space](https://mempool.spa - Receiving sound notification when a new block appears in the mainnet or testnet of the Bitcoin network +- Receiving a sound notification when the fee in the network crosses a specified value - Obtaining information about current fees in the Bitcoin network - Obtaining information about the time the last block appeared +## Installation + +**Click [here][1], then click "Add to Chrome"** + +> * Developed and tested for **Google Chrome** +> * Can be installed on any Chromium browser - Opera (GX), Vivaldi, etc. +> * In Microsoft Edge, click "Allow extensions from other stores" fisrt (is asked) + +### Firefox version when? + +There are currently no plans to support Firefox. + +1. Firefox does not support some crucial functions *(Background service workers)*. +2. Partial support will require an extensive tooling changes. +3. There is no demand for the Firefox version. + ## Local installation ### Prerequisites -1. You need to preinstall `node@^20.x` -2. Compatible `npm` must be installed -3. In the terminal, run the `npm install` command from the project folder +1. Download [latest release][2] or the whole repository +2. Install Node.js (required version in [package.json](./package.json)) +3. Compatible `npm` must be installed +4. In the terminal, run the `npm install` command from the project folder ### Linters To monitor the quality of the code, the project provides for the connection of linters. @@ -49,7 +72,7 @@ Rules for ESLint are specified in the `/.eslintrc` file. Code checking using ESLint starts with the command `npm run eslint`. #### Stylelint -Rules for Stylelint are specified in the `kalita/.stylelintrc.json` file. +Rules for Stylelint are specified in the `/.stylelintrc.json` file. Styles checking using Stylelint starts with the command `npm run stylelint`. @@ -73,3 +96,6 @@ Built project is stored in the `/dist` folder. ### Load extension to Chrome Load `dist` directory on Chrome extension page ([instruction](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked)) + +[1]: https://chromewebstore.google.com/detail/bitcoin-blocks-tracker/jhdbfjhembciojemihcimllmbibiakim +[2]: https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases diff --git a/package-lock.json b/package-lock.json index de9849f..6cd0989 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,62 +1,66 @@ { "name": "mempool-space-block-tracker-chrome-extenstion", - "version": "1.0.2", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mempool-space-block-tracker-chrome-extenstion", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "dependencies": { - "axios": "1.7.2", + "axios": "1.7.3", "classnames": "2.5.1", "react": "18.3.1", "react-dom": "18.3.1" }, "devDependencies": { - "@types/chrome": "0.0.268", - "@types/react": "18.3.2", + "@types/chrome": "0.0.269", + "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "7.9.0", - "@typescript-eslint/parser": "7.9.0", - "autoprefixer": "10.4.19", + "@types/ws": "8.5.12", + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "autoprefixer": "10.4.20", "clean-webpack-plugin": "4.0.0", "copy-webpack-plugin": "12.0.2", - "css-loader": "7.1.1", - "cssnano": "7.0.1", + "css-loader": "7.1.2", + "cssnano": "7.0.4", "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "18.0.0", "eslint-config-prettier": "9.1.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "2.29.1", - "eslint-plugin-jsx-a11y": "6.8.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-react": "7.34.1", + "eslint-plugin-jsx-a11y": "6.9.0", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-simple-import-sort": "12.1.0", - "eslint-plugin-unicorn": "53.0.0", + "eslint-plugin-simple-import-sort": "12.1.1", + "eslint-plugin-unicorn": "55.0.0", "fork-ts-checker-webpack-plugin": "9.0.2", - "glob": "10.3.15", + "glob": "11.0.0", "html-webpack-plugin": "5.6.0", + "husky": "9.1.4", "mini-css-extract-plugin": "2.9.0", "postcss-loader": "8.1.1", - "prettier": "3.2.5", - "sass": "1.77.1", - "sass-loader": "14.2.1", + "prettier": "3.3.3", + "sass": "1.77.8", + "sass-loader": "16.0.0", "style-loader": "4.0.0", - "stylelint": "16.5.0", - "stylelint-config-sass-guidelines": "11.1.0", + "stylelint": "16.8.1", + "stylelint-config-sass-guidelines": "12.0.0", "stylelint-order": "6.0.4", - "stylelint-scss": "6.3.0", + "stylelint-scss": "6.5.0", "ts-loader": "9.5.1", "ts-node": "10.9.2", - "typescript": "5.4.5", - "webpack": "5.91.0", + "typescript": "5.5.4", + "webpack": "5.93.0", "webpack-cli": "5.1.4", - "webpack-merge": "5.10.0" + "webpack-merge": "6.0.1" + }, + "engines": { + "node": ">=20" } }, "node_modules/@babel/code-frame": { @@ -165,18 +169,6 @@ "node": ">=4" } }, - "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -200,9 +192,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", - "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz", + "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==", "dev": true, "funding": [ { @@ -214,17 +206,18 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-tokenizer": "^2.4.1" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz", - "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz", + "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==", "dev": true, "funding": [ { @@ -236,14 +229,15 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18" } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz", - "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==", + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz", + "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==", "dev": true, "funding": [ { @@ -255,12 +249,13 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1" } }, "node_modules/@csstools/selector-specificity": { @@ -425,6 +420,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -442,6 +438,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -449,29 +446,19 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -489,6 +476,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -499,23 +487,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -614,6 +585,7 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -624,6 +596,7 @@ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -643,11 +616,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@stylistic/stylelint-plugin": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.3.tgz", + "integrity": "sha512-/KUcqX36AbbUk7KvNuM0dWv2XSlPa1M12CPcC//eA4MNEFsZFl+2Kf8UZCLjlIWIrDNitd591vaVkXfOwUtsFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/media-query-list-parser": "^2.1.13", + "is-plain-object": "^5.0.0", + "postcss-selector-parser": "^6.1.1", + "postcss-value-parser": "^4.2.0", + "style-search": "^0.1.0", + "stylelint": "^16.8.0" + }, + "engines": { + "node": "^18.12 || >=20.9" + }, + "peerDependencies": { + "stylelint": "^16.0.2" + } + }, + "node_modules/@stylistic/stylelint-plugin/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -677,10 +684,11 @@ "dev": true }, "node_modules/@types/chrome": { - "version": "0.0.268", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.268.tgz", - "integrity": "sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA==", + "version": "0.0.269", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.269.tgz", + "integrity": "sha512-vF7x8YywnhXX2F06njQ/OE7a3Qeful43C5GUOsUksXWk89WoSFUU3iLeZW8lDpVO9atm8iZIEiLQTRC3H7NOXQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" @@ -786,10 +794,11 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.3.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", - "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "dev": true, + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -805,25 +814,27 @@ } }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", - "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/type-utils": "7.9.0", - "@typescript-eslint/utils": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -847,15 +858,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" }, "engines": { @@ -875,13 +887,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -892,13 +905,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", - "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -919,10 +933,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -932,13 +947,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -964,15 +980,17 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -984,10 +1002,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -996,15 +1015,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1018,12 +1038,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1254,11 +1275,12 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^8" } @@ -1395,12 +1417,13 @@ "dev": true }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/array-buffer-byte-length": { @@ -1533,29 +1556,21 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/arraybuffer.prototype.slice": { @@ -1602,9 +1617,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -1620,12 +1635,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -1654,18 +1670,19 @@ } }, "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, + "license": "MPL-2.0", "engines": { "node": ">=4" } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1674,12 +1691,13 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/balanced-match": { @@ -1717,21 +1735,22 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -1747,11 +1766,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -1821,6 +1841,7 @@ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", @@ -1829,9 +1850,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001618", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", - "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", + "version": "1.0.30001649", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz", + "integrity": "sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==", "dev": true, "funding": [ { @@ -1846,7 +1867,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -2169,6 +2191,7 @@ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "dev": true, + "license": "ISC", "engines": { "node": "^14 || ^16 || >=18" }, @@ -2186,10 +2209,11 @@ } }, "node_modules/css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -2286,13 +2310,14 @@ } }, "node_modules/cssnano": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.1.tgz", - "integrity": "sha512-917Mej/4SdI7b55atsli3sU4MOJ9XDoKgnlCtQtXYj8XUFcM3riTuYHyqBBnnskawW+zWwp0KxJzpEUodlpqUg==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.4.tgz", + "integrity": "sha512-rQgpZra72iFjiheNreXn77q1haS2GEy69zCMbu4cpXCFPMQF+D4Ik5V7ktMzUF/sA7xCIgcqHwGPnCD+0a1vHg==", "dev": true, + "license": "MIT", "dependencies": { - "cssnano-preset-default": "^7.0.1", - "lilconfig": "^3.1.1" + "cssnano-preset-default": "^7.0.4", + "lilconfig": "^3.1.2" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -2306,41 +2331,42 @@ } }, "node_modules/cssnano-preset-default": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.1.tgz", - "integrity": "sha512-Fumyr+uZMcjYQeuHssAZxn0cKj3cdQc5GcxkBcmEzISGB+UW9CLNlU4tBOJbJGcPukFDlicG32eFbrc8K9V5pw==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.4.tgz", + "integrity": "sha512-jQ6zY9GAomQX7/YNLibMEsRZguqMUGuupXcEk2zZ+p3GUxwCAsobqPYE62VrJ9qZ0l9ltrv2rgjwZPBIFIjYtw==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "css-declaration-sorter": "^7.2.0", "cssnano-utils": "^5.0.0", "postcss-calc": "^10.0.0", - "postcss-colormin": "^7.0.0", - "postcss-convert-values": "^7.0.0", - "postcss-discard-comments": "^7.0.0", + "postcss-colormin": "^7.0.1", + "postcss-convert-values": "^7.0.2", + "postcss-discard-comments": "^7.0.1", "postcss-discard-duplicates": "^7.0.0", "postcss-discard-empty": "^7.0.0", "postcss-discard-overridden": "^7.0.0", - "postcss-merge-longhand": "^7.0.0", - "postcss-merge-rules": "^7.0.0", + "postcss-merge-longhand": "^7.0.2", + "postcss-merge-rules": "^7.0.2", "postcss-minify-font-values": "^7.0.0", "postcss-minify-gradients": "^7.0.0", - "postcss-minify-params": "^7.0.0", - "postcss-minify-selectors": "^7.0.0", + "postcss-minify-params": "^7.0.1", + "postcss-minify-selectors": "^7.0.2", "postcss-normalize-charset": "^7.0.0", "postcss-normalize-display-values": "^7.0.0", "postcss-normalize-positions": "^7.0.0", "postcss-normalize-repeat-style": "^7.0.0", "postcss-normalize-string": "^7.0.0", "postcss-normalize-timing-functions": "^7.0.0", - "postcss-normalize-unicode": "^7.0.0", + "postcss-normalize-unicode": "^7.0.1", "postcss-normalize-url": "^7.0.0", "postcss-normalize-whitespace": "^7.0.0", - "postcss-ordered-values": "^7.0.0", - "postcss-reduce-initial": "^7.0.0", + "postcss-ordered-values": "^7.0.1", + "postcss-reduce-initial": "^7.0.1", "postcss-reduce-transforms": "^7.0.0", - "postcss-svgo": "^7.0.0", - "postcss-unique-selectors": "^7.0.0" + "postcss-svgo": "^7.0.1", + "postcss-unique-selectors": "^7.0.1" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -2354,6 +2380,7 @@ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz", "integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -2366,6 +2393,7 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -2379,6 +2407,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -2392,7 +2421,8 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { "version": "3.1.2", @@ -2458,10 +2488,11 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -2474,6 +2505,39 @@ } } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2619,15 +2683,6 @@ "node": ">=0.4.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -2739,13 +2794,15 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.769", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.769.tgz", - "integrity": "sha512-bZu7p623NEA2rHTc9K1vykl57ektSPQYFFqQir8BOYf6EKOB+yIsbFB9Kpm7Cgt6tsLr9sRkqfqSZUw7LP1XxQ==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz", + "integrity": "sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2754,10 +2811,11 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -2886,6 +2944,27 @@ "node": ">= 0.4" } }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-iterator-helpers": { "version": "1.0.19", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", @@ -2974,6 +3053,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3249,27 +3329,28 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", + "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", + "axe-core": "^4.9.1", + "axobject-query": "~3.1.1", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" @@ -3285,13 +3366,14 @@ "dev": true }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -3315,35 +3397,36 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", - "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { @@ -3406,27 +3489,29 @@ } }, "node_modules/eslint-plugin-simple-import-sort": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz", - "integrity": "sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "dev": true, + "license": "MIT", "peerDependencies": { "eslint": ">=5.0.0" } }, "node_modules/eslint-plugin-unicorn": { - "version": "53.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz", - "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==", + "version": "55.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", + "integrity": "sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.24.5", "@eslint-community/eslint-utils": "^4.4.0", - "@eslint/eslintrc": "^3.0.2", "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", "core-js-compat": "^3.37.0", "esquery": "^1.5.0", + "globals": "^15.7.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", "jsesc": "^3.0.2", @@ -3447,29 +3532,6 @@ "eslint": ">=8.56.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz", - "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", @@ -3485,47 +3547,6 @@ "node": ">=8" } }, - "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/espree": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", - "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -3854,10 +3875,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4162,22 +4184,24 @@ } }, "node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.11.0" + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4206,20 +4230,22 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4263,6 +4289,19 @@ "which": "bin/which" } }, + "node_modules/globals": { + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -4518,7 +4557,23 @@ "entities": "^2.0.0" } }, - "node_modules/icss-utils": { + "node_modules/husky": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", + "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", @@ -4652,6 +4707,23 @@ "node": ">=10.13.0" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -4884,6 +4956,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5120,15 +5193,16 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5233,10 +5307,11 @@ } }, "node_modules/known-css-properties": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz", - "integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==", - "dev": true + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "dev": true, + "license": "MIT" }, "node_modules/language-subtag-registry": { "version": "0.3.22", @@ -5270,10 +5345,11 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -5318,7 +5394,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5336,7 +5413,8 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -5358,6 +5436,16 @@ "tslib": "^2.0.3" } }, + "node_modules/lru-cache": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", + "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -5420,12 +5508,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -5502,10 +5591,11 @@ } }, "node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -5563,10 +5653,11 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "2.5.0", @@ -5637,6 +5728,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5710,23 +5818,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -5830,6 +5921,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -5920,30 +6018,22 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6032,9 +6122,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -6050,9 +6140,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -6060,12 +6151,13 @@ } }, "node_modules/postcss-calc": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.0.tgz", - "integrity": "sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.1.tgz", + "integrity": "sha512-pp1Z3FxtxA+xHAoWXcOXgnBN1WPu4ZiJ5LWGjKyf9MMreagAsaTUtnqFK1y1sHhyJddAkYTPu6XSuLgb3oYCjw==", "dev": true, + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.16", + "postcss-selector-parser": "^6.1.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -6076,12 +6168,13 @@ } }, "node_modules/postcss-colormin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.0.tgz", - "integrity": "sha512-5CN6fqtsEtEtwf3mFV3B4UaZnlYljPpzmGeDB4yCK067PnAtfLe9uX2aFZaEwxHE7HopG5rUkW8gyHrNAesHEg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.1.tgz", + "integrity": "sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0", "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" @@ -6094,12 +6187,13 @@ } }, "node_modules/postcss-convert-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.0.tgz", - "integrity": "sha512-bMuzDgXBbFbByPgj+/r6va8zNuIDUaIIbvAFgdO1t3zdgJZ77BZvu6dfWyd6gHEJnYzmeVr9ayUsAQL3/qLJ0w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.2.tgz", + "integrity": "sha512-MuZIF6HJ4izko07Q0TgW6pClalI4al6wHRNPkFzqQdwAwG7hPn0lA58VZdxyb2Vl5AYjJ1piO+jgF9EnTjQwQQ==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -6110,10 +6204,14 @@ } }, "node_modules/postcss-discard-comments": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.0.tgz", - "integrity": "sha512-xpSdzRqYmy4YIVmjfGyYXKaI1SRnK6CTr+4Zmvyof8ANwvgfZgGdVtmgAvzh59gJm808mJCWQC9tFN0KF5dEXA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.1.tgz", + "integrity": "sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==", "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.0" + }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -6126,6 +6224,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.0.tgz", "integrity": "sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -6138,6 +6237,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz", "integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -6150,6 +6250,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz", "integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==", "dev": true, + "license": "MIT", "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -6233,13 +6334,14 @@ "dev": true }, "node_modules/postcss-merge-longhand": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.0.tgz", - "integrity": "sha512-0X8I4/9+G03X5/5NnrfopG/YEln2XU8heDh7YqBaiq2SeaKIG3n66ShZPjIolmVuLBQ0BEm3yS8o1mlCLHdW7A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.2.tgz", + "integrity": "sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^7.0.0" + "stylehacks": "^7.0.2" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -6249,15 +6351,16 @@ } }, "node_modules/postcss-merge-rules": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.0.tgz", - "integrity": "sha512-Zty3VlOsD6VSjBMu6PiHCVpLegtBT/qtZRVBcSeyEZ6q1iU5qTYT0WtEoLRV+YubZZguS5/ycfP+NRiKfjv6aw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.2.tgz", + "integrity": "sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0", "cssnano-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.16" + "postcss-selector-parser": "^6.1.0" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -6271,6 +6374,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz", "integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6286,6 +6390,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz", "integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==", "dev": true, + "license": "MIT", "dependencies": { "colord": "^2.9.3", "cssnano-utils": "^5.0.0", @@ -6299,12 +6404,13 @@ } }, "node_modules/postcss-minify-params": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.0.tgz", - "integrity": "sha512-XOJAuX8Q/9GT1sGxlUvaFEe2H9n50bniLZblXXsAT/BwSfFYvzSZeFG7uupwc0KbKpTnflnQ7aMwGzX6JUWliQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.1.tgz", + "integrity": "sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" }, @@ -6316,12 +6422,14 @@ } }, "node_modules/postcss-minify-selectors": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.0.tgz", - "integrity": "sha512-f00CExZhD6lNw2vTZbcnmfxVgaVKzUw6IRsIFX3JTT8GdsoABc1WnhhGwL1i8YPJ3sSWw39fv7XPtvLb+3Uitw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.2.tgz", + "integrity": "sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==", "dev": true, + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.16" + "cssesc": "^3.0.0", + "postcss-selector-parser": "^6.1.0" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -6394,6 +6502,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz", "integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -6406,6 +6515,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz", "integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6421,6 +6531,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz", "integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6436,6 +6547,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz", "integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6451,6 +6563,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz", "integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6466,6 +6579,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz", "integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6477,12 +6591,13 @@ } }, "node_modules/postcss-normalize-unicode": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.0.tgz", - "integrity": "sha512-OnKV52/VFFDAim4n0pdI+JAhsolLBdnCKxE6VV5lW5Q/JeVGFN8UM8ur6/A3EAMLsT1ZRm3fDHh/rBoBQpqi2w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.1.tgz", + "integrity": "sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -6497,6 +6612,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz", "integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6512,6 +6628,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz", "integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6523,10 +6640,11 @@ } }, "node_modules/postcss-ordered-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.0.tgz", - "integrity": "sha512-KROvC63A8UQW1eYDljQe1dtwc1E/M+mMwDT6z7khV/weHYLWTghaLRLunU7x1xw85lWFwVZOAGakxekYvKV+0w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz", + "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==", "dev": true, + "license": "MIT", "dependencies": { "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" @@ -6539,12 +6657,13 @@ } }, "node_modules/postcss-reduce-initial": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.0.tgz", - "integrity": "sha512-iqGgmBxY9LrblZ0BKLjmrA1mC/cf9A/wYCCqSmD6tMi+xAyVl0+DfixZIHSVDMbCPRPjNmVF0DFGth/IDGelFQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.1.tgz", + "integrity": "sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0" }, "engines": { @@ -6559,6 +6678,7 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz", "integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -6570,10 +6690,11 @@ } }, "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", - "dev": true + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.4.tgz", + "integrity": "sha512-R6vHqZWgVnTAPq0C+xjyHfEZqfIYboCBVSy24MjxEDm+tIh1BU4O6o7DP7AA7kHzf136d+Qc5duI4tlpHjixDw==", + "dev": true, + "license": "MIT" }, "node_modules/postcss-safe-parser": { "version": "7.0.0", @@ -6628,10 +6749,11 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6650,13 +6772,14 @@ } }, "node_modules/postcss-svgo": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.0.tgz", - "integrity": "sha512-Xj5DRdvA97yRy3wjbCH2NKXtDUwEnph6EHr5ZXszsBVKCNrKXYBjzAXqav7/Afz5WwJ/1peZoTguCEJIg7ytmA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz", + "integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" + "svgo": "^3.3.2" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >= 18" @@ -6666,12 +6789,13 @@ } }, "node_modules/postcss-unique-selectors": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.0.tgz", - "integrity": "sha512-NYFqcft7vVQMZlQPsMdMPy+qU/zDpy95Malpw4GeA9ZZjM6dVXDshXtDmLc0m4WCD6XeZCJqjTfPT1USsdt+rA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.1.tgz", + "integrity": "sha512-MH7QE/eKUftTB5ta40xcHLl7hkZjgDFydpfTK+QWXeHxghVt3VoPqYL5/G+zYZPPIs+8GuqFXSTgxBSoB1RZtQ==", "dev": true, + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.16" + "postcss-selector-parser": "^6.1.0" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -6696,10 +6820,11 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -6911,12 +7036,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -7167,10 +7286,11 @@ } }, "node_modules/sass": { - "version": "1.77.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.1.tgz", - "integrity": "sha512-OMEyfirt9XEfyvocduUIOlUSkWOXS/LAt6oblR/ISXCTukyavjex+zQNm51pPCOiFKY1QpWvEH1EeCkgyV3I6w==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -7184,10 +7304,11 @@ } }, "node_modules/sass-loader": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", - "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", "dev": true, + "license": "MIT", "dependencies": { "neo-async": "^2.6.2" }, @@ -7473,6 +7594,19 @@ "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -7493,6 +7627,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7502,6 +7637,17 @@ "node": ">=8" } }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -7528,6 +7674,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -7595,6 +7752,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7642,14 +7800,22 @@ "webpack": "^5.27.0" } }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true, + "license": "ISC" + }, "node_modules/stylehacks": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.0.tgz", - "integrity": "sha512-47Nw4pQ6QJb4CA6dzF2m9810sjQik4dfk4UwAm5wlwhrW3syzZKF8AR4/cfO3Cr6lsFgAoznQq0Wg57qhjTA2A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.2.tgz", + "integrity": "sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" + "browserslist": "^4.23.1", + "postcss-selector-parser": "^6.1.0" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -7659,25 +7825,36 @@ } }, "node_modules/stylelint": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.5.0.tgz", - "integrity": "sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.1.tgz", + "integrity": "sha512-O8aDyfdODSDNz/B3gW2HQ+8kv8pfhSu7ZR7xskQ93+vI6FhKKGUJMQ03Ydu+w3OvXXE0/u4hWU4hCPNOyld+OA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4", - "@csstools/media-query-list-parser": "^2.1.9", - "@csstools/selector-specificity": "^3.0.3", - "@dual-bundle/import-meta-resolve": "^4.0.0", + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1", + "@csstools/media-query-list-parser": "^2.1.13", + "@csstools/selector-specificity": "^3.1.1", + "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.2", "css-tree": "^2.3.1", - "debug": "^4.3.4", + "debug": "^4.3.6", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "^9.0.0", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", @@ -7685,16 +7862,16 @@ "ignore": "^5.3.1", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.30.0", + "known-css-properties": "^0.34.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", - "micromatch": "^4.0.5", + "micromatch": "^4.0.7", "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.38", - "postcss-resolve-nested-selector": "^0.1.1", + "picocolors": "^1.0.1", + "postcss": "^8.4.40", + "postcss-resolve-nested-selector": "^0.1.4", "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.0.16", + "postcss-selector-parser": "^6.1.1", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -7709,18 +7886,16 @@ }, "engines": { "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" } }, "node_modules/stylelint-config-sass-guidelines": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/stylelint-config-sass-guidelines/-/stylelint-config-sass-guidelines-11.1.0.tgz", - "integrity": "sha512-mVE3UmN8MlshK4Gb3eYk6f8tw9DkQ9yjMF4W9krlmpaNZpSXOdh13AL0sU7l/9l4Pnpt4KMobNNIRI0tJl56Cw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-sass-guidelines/-/stylelint-config-sass-guidelines-12.0.0.tgz", + "integrity": "sha512-lGJml+QEVlU/nqI+awiQieyxXHkmuwhz4XsfUNkQVcNaPXBpLgefOHjZ7ZSmUm4y4YG8JhrvYNjYzUcTfe8cdg==", "dev": true, + "license": "MIT", "dependencies": { + "@stylistic/stylelint-plugin": "^2.1.0", "postcss-scss": "^4.0.9", "stylelint-scss": "^6.2.1" }, @@ -7746,15 +7921,18 @@ } }, "node_modules/stylelint-scss": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.3.0.tgz", - "integrity": "sha512-8OSpiuf1xC7f8kllJsBOFAOYp/mR/C1FXMVeOFjtJPw+AFvEmC93FaklHt7MlOqU4poxuQ1TkYMyfI0V+1SxjA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.5.0.tgz", + "integrity": "sha512-yOnYlr71wrTPT3rYyUurgTj6Rw7JUtzsZQsiPEjvs+k/yqoYHdweqpw6XN/ARpxjAuvJpddoMUvV8aAIpvUwTg==", "dev": true, + "license": "MIT", "dependencies": { - "known-css-properties": "^0.30.0", + "css-tree": "2.3.1", + "is-plain-object": "5.0.0", + "known-css-properties": "^0.34.0", "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.15", + "postcss-resolve-nested-selector": "^0.1.4", + "postcss-selector-parser": "^6.1.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -7764,6 +7942,16 @@ "stylelint": "^16.0.2" } }, + "node_modules/stylelint-scss/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stylelint/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -7809,28 +7997,30 @@ } }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "flat-cache": "^5.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.2.9", + "flatted": "^3.3.1", "keyv": "^4.5.4" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/stylelint/node_modules/is-plain-object": { @@ -7905,6 +8095,7 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -7930,6 +8121,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -7939,6 +8131,7 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -7955,6 +8148,7 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -7969,6 +8163,7 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -7984,6 +8179,7 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -7998,6 +8194,7 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -8006,10 +8203,11 @@ } }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, + "license": "MIT", "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" @@ -8188,6 +8386,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -8437,10 +8636,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8486,9 +8686,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -8504,6 +8704,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.2", "picocolors": "^1.0.1" @@ -8566,10 +8767,11 @@ } }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -8577,10 +8779,10 @@ "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -8666,11 +8868,12 @@ "node": ">=14" } }, - "node_modules/webpack-merge": { + "node_modules/webpack-cli/node_modules/webpack-merge": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", @@ -8680,6 +8883,21 @@ "node": ">=10.0.0" } }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", @@ -8816,12 +9034,31 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8834,6 +9071,73 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index f614a68..6538adf 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "name": "mempool-space-block-tracker-chrome-extenstion", "version": "1.1.0", "description": "Chrome extension to track new bitcoin blocks via Mempool Space", + "author": "Savoskin Ivan", + "license": "MIT", "scripts": { "dev": "webpack --watch --progress --mode=development", "prod": "npm run lint && npm run build:prod", @@ -12,58 +14,79 @@ "stylelint": "stylelint \"src/**/*.scss\"", "lint:fix": "npm run eslint:fix && npm run stylelint:fix", "eslint:fix": "eslint --ext .ts,.tsx src --color --fix", - "stylelint:fix": "stylelint \"src/**/*.scss\" --fix" + "stylelint:fix": "stylelint \"src/**/*.scss\" --fix", + "husky": "husky" + }, + "engines": { + "node": ">=20" + }, + "lint-staged": { + "*.{ts,tsx}": [ + "eslint --fix", + "git add" + ], + "*.{css,scss}": [ + "stylelint --fix", + "git add" + ] }, - "author": "Savoskin Ivan", - "license": "MIT", "dependencies": { - "axios": "1.7.2", + "axios": "1.7.3", "classnames": "2.5.1", "react": "18.3.1", "react-dom": "18.3.1" }, "devDependencies": { - "@types/chrome": "0.0.268", - "@types/react": "18.3.2", + "@types/chrome": "0.0.269", + "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "7.9.0", - "@typescript-eslint/parser": "7.9.0", - "autoprefixer": "10.4.19", + "@types/ws": "8.5.12", + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "autoprefixer": "10.4.20", "copy-webpack-plugin": "12.0.2", "clean-webpack-plugin": "4.0.0", - "css-loader": "7.1.1", - "cssnano": "7.0.1", + "css-loader": "7.1.2", + "cssnano": "7.0.4", "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "18.0.0", "eslint-config-prettier": "9.1.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "2.29.1", - "eslint-plugin-jsx-a11y": "6.8.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-react": "7.34.1", + "eslint-plugin-jsx-a11y": "6.9.0", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-simple-import-sort": "12.1.0", - "eslint-plugin-unicorn": "53.0.0", + "eslint-plugin-simple-import-sort": "12.1.1", + "eslint-plugin-unicorn": "55.0.0", "fork-ts-checker-webpack-plugin": "9.0.2", "html-webpack-plugin": "5.6.0", - "glob": "10.3.15", + "husky": "9.1.4", + "glob": "11.0.0", "mini-css-extract-plugin": "2.9.0", "postcss-loader": "8.1.1", - "prettier": "3.2.5", - "sass": "1.77.1", - "sass-loader": "14.2.1", + "prettier": "3.3.3", + "sass": "1.77.8", + "sass-loader": "16.0.0", "style-loader": "4.0.0", - "stylelint": "16.5.0", - "stylelint-config-sass-guidelines": "11.1.0", + "stylelint": "16.8.1", + "stylelint-config-sass-guidelines": "12.0.0", "stylelint-order": "6.0.4", - "stylelint-scss": "6.3.0", + "stylelint-scss": "6.5.0", "ts-loader": "9.5.1", "ts-node": "10.9.2", - "typescript": "5.4.5", - "webpack": "5.91.0", + "typescript": "5.5.4", + "webpack": "5.93.0", "webpack-cli": "5.1.4", - "webpack-merge": "5.10.0" + "webpack-merge": "6.0.1" + }, + "webExt": { + "sourceDir": "dist", + "run": { + "startUrl": [ + "https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension" + ] + } } } diff --git a/privacy-policy.md b/privacy-policy.md new file mode 100644 index 0000000..34eaa92 --- /dev/null +++ b/privacy-policy.md @@ -0,0 +1,7 @@ +# Privacy Policy for Bitcoin blocks tracker + +No data or personal information is collected by Bitcoin blocks tracker. + +##### Contact + +If you have any questions or suggestions regarding this privacy policy, do not hesitate to [contact me](https://t.me/IvanSavoskin). diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json new file mode 100644 index 0000000..8e648ef --- /dev/null +++ b/public/_locales/en/messages.json @@ -0,0 +1,107 @@ +{ + "trackingState": { + "message": "Tracking state" + }, + "blocks": { + "message": "Blocks" + }, + "fee": { + "message": "Fee" + }, + "on": { + "message": "ON" + }, + "off": { + "message": "OFF" + }, + "blockchain": { + "message": "Blockchain" + }, + "mainnet": { + "message": "Mainnet" + }, + "testnet": { + "message": "Testnet" + }, + "loading":{ + "message": "Loading..." + }, + "trackingIsDisabled": { + "message": "Tracking is disabled" + }, + "feesInfo": { + "message": "Fees info" + }, + "slow": { + "message": "Slow" + }, + "medium": { + "message": "Medium" + }, + "fast": { + "message": "Fast" + }, + "satPerVb": { + "message": "sat/Vb" + }, + "lastBlockInfo": { + "message": "Last block info" + }, + "lastBlockHeight": { + "message": "Last block height:" + }, + "lastBlockMined": { + "message": "Last block mined:" + }, + "lessMinuteAgo": { + "message": "< minute ago" + }, + "oneMinuteAgo": { + "message": "minutes ago" + }, + "fewMinutesAgo": { + "message": "minutes ago" + }, + "manyMinutesAgo": { + "message": "minutes ago" + }, + "soundSelectorIncorrectFormatError": { + "message": "Could not use this sound: incorrect format" + }, + "soundSelectorInputPlaceholder": { + "message": "Sound link" + }, + "notificationSettings": { + "message": "Notification settings" + }, + "blockNotificationSettingsDescription": { + "message": "Settings for notifications when a new block appears." + }, + "blockNotificationVolumeControlLabel": { + "message": "Block notification volume control" + }, + "blockNotificationSoundSelectorLabel": { + "message": "Block notification sound" + }, + "feesNotificationSettingsDescription": { + "message": "Settings for notifications when fee reaches a specified value." + }, + "feesNotificationVolumeControlLabel": { + "message": "Fee notification volume control" + }, + "feesNotificationSoundSelectorLabel": { + "message": "Fee notification sound" + }, + "feeNotificationBorderCheckboxLabel": { + "message": "Crossing border notification" + }, + "feeNotificationBorderInputLabel": { + "message": "Fee border" + }, + "feeNotificationBorderInputPlaceholder": { + "message": "Fee border" + }, + "releaseNotesNotificationTitle": { + "message": "Release Notes" + } +} \ No newline at end of file diff --git a/public/_locales/ru/messages.json b/public/_locales/ru/messages.json new file mode 100644 index 0000000..7ec87be --- /dev/null +++ b/public/_locales/ru/messages.json @@ -0,0 +1,107 @@ +{ + "trackingState": { + "message": "Отслеживание состояния" + }, + "blocks": { + "message": "Блоки" + }, + "fee": { + "message": "Комиссии" + }, + "on": { + "message": "ВКЛ" + }, + "off": { + "message": "ВЫКЛ" + }, + "blockchain": { + "message": "Блокчейн" + }, + "mainnet": { + "message": "Мейннет" + }, + "testnet": { + "message": "Тестнет" + }, + "loading":{ + "message": "Загрузка..." + }, + "trackingIsDisabled": { + "message": "Отслеживание отключено" + }, + "feesInfo": { + "message": "Информация о комиссиях" + }, + "slow": { + "message": "Медленная" + }, + "medium": { + "message": "Средняя" + }, + "fast": { + "message": "Быстрая" + }, + "satPerVb": { + "message": "sat/Vb" + }, + "lastBlockInfo": { + "message": "Информация о последнем блоке" + }, + "lastBlockHeight": { + "message": "Номер последнего блока:" + }, + "lastBlockMined": { + "message": "Время последнего блока:" + }, + "lessMinuteAgo": { + "message": "< меньше минуты назад" + }, + "oneMinuteAgo": { + "message": "минуту назад" + }, + "fewMinutesAgo": { + "message": "минуты назад" + }, + "manyMinutesAgo": { + "message": "минут назад" + }, + "soundSelectorIncorrectFormatError": { + "message": "Не удалось использовать этот звук: неправильный формат" + }, + "soundSelectorInputPlaceholder": { + "message": "Ссылка на звук" + }, + "notificationSettings": { + "message": "Настройки уведомлений" + }, + "blockNotificationSettingsDescription": { + "message": "Настройки уведомлений при появлении нового блока." + }, + "blockNotificationVolumeControlLabel": { + "message": "Управление громкостью уведомлений для блока" + }, + "blockNotificationSoundSelectorLabel": { + "message": "Звук уведомления о блоке" + }, + "feesNotificationSettingsDescription": { + "message": "Настройки уведомлений при достижении комиссией указанного значения." + }, + "feesNotificationVolumeControlLabel": { + "message": "Регулятор громкости уведомления о комиссии" + }, + "feesNotificationSoundSelectorLabel": { + "message": "Звук уведомления о комиссии" + }, + "feeNotificationBorderCheckboxLabel": { + "message": "Уведомление о пересечении границы" + }, + "feeNotificationBorderInputLabel": { + "message": "Граница комиссии" + }, + "feeNotificationBorderInputPlaceholder": { + "message": "Граница комиссии" + }, + "releaseNotesNotificationTitle": { + "message": "Нововведения" + } +} \ No newline at end of file diff --git a/src/background/index.ts b/src/background/index.ts index e18f253..a237ed2 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,12 +1,8 @@ -import { sendMessage } from "@coreUtils/utils"; -import { - BackgroundMessage, - BlockPopupMessage, - FeeNotificationBorder, - Fees, - FeesPopupMessage, - PlayNotificationSoundOffscreenMessage -} from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { FeeNotificationBorder, Fees } from "@models/fee/types"; +import { BackgroundMessageType, MessageTarget, OffscreenMessageType, PopupMessageType } from "@models/messages/enums"; +import { BackgroundMessage, BlockPopupMessage, FeesPopupMessage, PlayNotificationSoundOffscreenMessage } from "@models/messages/types"; import logo from "@static/images/logo.png"; import { releaseNotes } from "./utils/releaseNotes"; @@ -28,6 +24,7 @@ let blockNotificationSound: string | null = null; let isFeeNotificationEnabled: boolean | null = null; let feeNotificationBorder: FeeNotificationBorder | null = null; +let feeNotificationBorderChangeState: boolean = false; let feeNotificationVolume: number = 100; let feeNotificationSound: string | null = null; @@ -39,15 +36,17 @@ chrome.runtime.onInstalled.addListener((details) => { const currentVersionReleaseNotes = releaseNotes[currentVersion]; + const uiLang = chrome.i18n.getUILanguage(); + if (currentVersionReleaseNotes) { chrome.notifications.create( - `relese-notes-${currentVersion}`, + `release-notes-${currentVersion}`, { type: "basic", iconUrl: logo, title: currentVersionReleaseNotes.title, - message: currentVersionReleaseNotes.message, - buttons: [{ title: "Release Notes" }] + message: uiLang === "ru" ? currentVersionReleaseNotes.message.ru : currentVersionReleaseNotes.message.en, + buttons: [{ title: translate("releaseNotesNotificationTitle") }] }, () => {} ); @@ -100,6 +99,7 @@ function initialSettings(): void { "blockNotificationVolume", "blockNotificationSound", "feeNotificationBorder", + "feeNotificationBorderChangeState", "feeNotificationVolume", "feeNotificationSound" ]) @@ -107,6 +107,7 @@ function initialSettings(): void { blockNotificationVolume = result.blockNotificationVolume ?? 100; blockNotificationSound = result.blockNotificationSound; feeNotificationBorder = result.feeNotificationBorder; + feeNotificationBorderChangeState = result.feeNotificationBorderChangeState ?? false; feeNotificationVolume = result.feeNotificationVolume ?? 100; feeNotificationSound = result.feeNotificationSound; }); @@ -160,8 +161,8 @@ function onBlockMessageHandler(eventData: any): void { console.debug("Send block notification to offscreen"); sendMessage({ data: { volume: blockNotificationVolume, sound: blockNotificationSound }, - target: "offscreen", - type: "playBlockNotificationSound" + target: [MessageTarget.OFFSCREEN], + type: OffscreenMessageType.PLAY_BLOCK_NOTIFICATION_SOUND }); }); } @@ -172,9 +173,9 @@ function onBlockMessageHandler(eventData: any): void { console.debug("Update blocks info with new block"); sendMessage({ - target: "popup", + target: [MessageTarget.POPUP], data: { blockInfo: { lastBlockTime, lastBlockHeight } }, - type: "blockInfo" + type: PopupMessageType.BLOCK_INFO }); console.info("Block timestamp:", new Date(lastBlockTime).toLocaleString()); @@ -194,18 +195,30 @@ function checkFeeBorder(oldFees: Fees | null, newFees: Fees | null): void { oldFees[feeNotificationBorder.feeLevel] < newFees[feeNotificationBorder.feeLevel] && newFees[feeNotificationBorder.feeLevel] >= feeNotificationBorder.feeBorder && oldFees[feeNotificationBorder.feeLevel] < feeNotificationBorder.feeBorder; + const fromBorderToHighCondition = + oldFees[feeNotificationBorder.feeLevel] < newFees[feeNotificationBorder.feeLevel] && + newFees[feeNotificationBorder.feeLevel] >= feeNotificationBorder.feeBorder && + oldFees[feeNotificationBorder.feeLevel] === feeNotificationBorder.feeBorder; const fromHighToLowCondition = oldFees[feeNotificationBorder.feeLevel] > newFees[feeNotificationBorder.feeLevel] && newFees[feeNotificationBorder.feeLevel] <= feeNotificationBorder.feeBorder && oldFees[feeNotificationBorder.feeLevel] > feeNotificationBorder.feeBorder; + const fromBorderToLowCondition = + oldFees[feeNotificationBorder.feeLevel] > newFees[feeNotificationBorder.feeLevel] && + newFees[feeNotificationBorder.feeLevel] <= feeNotificationBorder.feeBorder && + oldFees[feeNotificationBorder.feeLevel] === feeNotificationBorder.feeBorder; - if (fromLowToHighCondition || fromHighToLowCondition) { + const condition = feeNotificationBorderChangeState + ? fromLowToHighCondition || fromHighToLowCondition || fromBorderToHighCondition || fromBorderToLowCondition + : fromLowToHighCondition || fromHighToLowCondition; + + if (condition) { setupOffscreenDocument().then(() => { console.debug("Send fee notification to offscreen"); sendMessage({ data: { volume: feeNotificationVolume, sound: feeNotificationSound }, - target: "offscreen", - type: "playFeeNotificationSound" + target: [MessageTarget.OFFSCREEN], + type: OffscreenMessageType.PLAY_FEE_NOTIFICATION_SOUND }); }); } @@ -213,12 +226,12 @@ function checkFeeBorder(oldFees: Fees | null, newFees: Fees | null): void { } function onFeesMessageHandler(eventData: any): void { - if ("fees" in eventData) { + if (PopupMessageType.FEES in eventData) { console.debug("Fee updated"); const lastFees = structuredClone(fees); fees = eventData.fees; - sendMessage({ target: "popup", data: { fees }, type: "fees" }); + sendMessage({ target: [MessageTarget.POPUP], data: { fees }, type: PopupMessageType.FEES }); checkFeeBorder(lastFees, fees); console.info(`Current fees: slow: ${fees?.hourFee}, medium: ${fees?.halfHourFee}, fast: ${fees?.fastestFee}`); @@ -233,9 +246,9 @@ function onBlocksMessageHandler(eventData: any): void { lastBlockHeight = lastBlock.height; lastBlockTime = lastBlock.timestamp * 1000; sendMessage({ - target: "popup", + target: [MessageTarget.POPUP], data: { blockInfo: { lastBlockTime, lastBlockHeight } }, - type: "blockInfo" + type: PopupMessageType.BLOCK_INFO }); } } @@ -305,11 +318,11 @@ function disableWebSocket(): void { lastBlockHeight = null; sendMessage({ - target: "popup", + target: [MessageTarget.POPUP], data: { blockInfo: { lastBlockTime, lastBlockHeight } }, - type: "blockInfo" + type: PopupMessageType.BLOCK_INFO }); - sendMessage({ target: "popup", data: { fees }, type: "fees" }); + sendMessage({ target: [MessageTarget.POPUP], data: { fees }, type: PopupMessageType.FEES }); } } @@ -322,7 +335,10 @@ chrome.storage.local.get(["isBlockNotificationEnabled", "isMainnet", "isFeeNotif }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeBlockNotificationEnabled") { + if ( + message.target.includes(MessageTarget.BACKGROUND) && + message.type === BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_ENABLED + ) { isBlockNotificationEnabled = message.data.enabled; if (isBlockNotificationEnabled) { enableWebSocket(!!isMainnet); @@ -333,7 +349,7 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeFeeNotificationEnabled") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.CHANGE_FEE_NOTIFICATION_ENABLED) { isFeeNotificationEnabled = message.data.enabled; if (isFeeNotificationEnabled) { enableWebSocket(!!isMainnet); @@ -344,7 +360,7 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeBlockchain") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.CHANGE_BLOCKCHAIN) { isMainnet = message.data.isMainnet; if ((isBlockNotificationEnabled || isFeeNotificationEnabled) && socket) { @@ -355,27 +371,30 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage, _, sendResponse) => { - if (message.target === "background" && message.type === "requestFees") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.REQUEST_FEES) { console.debug("Send initial fees info"); - sendResponse({ target: "popup", data: { fees }, type: "initialFees" }); + sendResponse({ target: [MessageTarget.POPUP], data: { fees }, type: PopupMessageType.INITIAL_FEES } as FeesPopupMessage); } }); chrome.runtime.onMessage.addListener((message: BackgroundMessage, _, sendResponse) => { - if (message.target === "background" && message.type === "requestLastBlockInfo") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.REQUEST_LAST_BLOCK_INFO) { console.debug("Send initial last block info"); sendResponse({ - target: "popup", + target: [MessageTarget.POPUP], data: { blockInfo: { lastBlockTime, lastBlockHeight } }, - type: "initialBlockInfo" - }); + type: PopupMessageType.INITIAL_BLOCK_INFO + } as BlockPopupMessage); } }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeBlockNotificationSoundVolume") { + if ( + message.target.includes(MessageTarget.BACKGROUND) && + message.type === BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND_VOLUME + ) { console.debug("Change block notification sound volume"); blockNotificationVolume = message.data.volume; @@ -383,7 +402,7 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeBlockNotificationSound") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND) { console.debug("Change block notification sound"); blockNotificationSound = message.data.sound; @@ -391,7 +410,10 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeFeeNotificationSoundVolume") { + if ( + message.target.includes(MessageTarget.BACKGROUND) && + message.type === BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND_VOLUME + ) { console.debug("Change fee notification sound volume"); feeNotificationVolume = message.data.volume; @@ -399,7 +421,7 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeFeeNotificationSound") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND) { console.debug("Change fee notification sound"); feeNotificationSound = message.data.sound; @@ -407,9 +429,20 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { }); chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { - if (message.target === "background" && message.type === "changeFeeNotificationBorder") { + if (message.target.includes(MessageTarget.BACKGROUND) && message.type === BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER) { console.debug("Change fee notification border"); feeNotificationBorder = message.data.feeBorder; } }); + +chrome.runtime.onMessage.addListener((message: BackgroundMessage) => { + if ( + message.target.includes(MessageTarget.BACKGROUND) && + message.type === BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER_CHANGE_STATE + ) { + console.debug("Change fee notification border change state"); + + feeNotificationBorderChangeState = message.data.feeNotificationBorderChangeState; + } +}); diff --git a/src/background/utils/releaseNotes.ts b/src/background/utils/releaseNotes.ts index 92d58d2..7e85a06 100644 --- a/src/background/utils/releaseNotes.ts +++ b/src/background/utils/releaseNotes.ts @@ -1,8 +1,18 @@ -export const releaseNotes: Record = { +export const releaseNotes: Record = { "1.1.0": { title: "Update 1.1.0", - message: - "Settings for notifications, commission tracking and other changes in version 1.1.0. More details at the link https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0", + message: { + en: "Settings for notifications, commission tracking and other changes in version 1.1.0. More details at the link https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0", + ru: "Настройки для уведомлений, отслеживания комиссий и другие изменения в версии 1.1.0. Подробнее на ссылке https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0" + }, link: "https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0" + }, + "1.2.0": { + title: "Update 1.2.0", + message: { + en: "Added localization into Russian, Added notification mode about the commission leaving the specified border and other changes in version 1.2.0. More details at the link https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.2.0", + ru: "Добавлена локализация на русский язык, добавлен режим нотификации о выходе комиссии с заданной границы и другие изменения в версии 1.2.0. Подробнее на ссылке https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.2.0" + }, + link: "https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.2.0" } }; diff --git a/src/models/block/types.ts b/src/models/block/types.ts new file mode 100644 index 0000000..64cacc8 --- /dev/null +++ b/src/models/block/types.ts @@ -0,0 +1,4 @@ +export interface BlockInfo { + lastBlockHeight: number | null; + lastBlockTime: number | null; +} diff --git a/src/models/fee/types.ts b/src/models/fee/types.ts new file mode 100644 index 0000000..3950de7 --- /dev/null +++ b/src/models/fee/types.ts @@ -0,0 +1,18 @@ +export interface Fees { + economyFee: number; + fastestFee: number; + halfHourFee: number; + hourFee: number; + minimumFee: number; +} + +export enum FeeLevel { + SLOW = "hourFee", + MEDIUM = "halfHourFee", + FAST = "fastestFee" +} + +export interface FeeNotificationBorder { + feeBorder: number | null; + feeLevel: FeeLevel; +} diff --git a/src/models/messages/enums.ts b/src/models/messages/enums.ts new file mode 100644 index 0000000..cef31ab --- /dev/null +++ b/src/models/messages/enums.ts @@ -0,0 +1,31 @@ +export enum MessageTarget { + BACKGROUND = "background", + OFFSCREEN = "offscreen", + POPUP = "popup" +} + +export enum BackgroundMessageType { + CHANGE_BLOCK_NOTIFICATION_ENABLED = "changeBlockNotificationEnabled", + CHANGE_BLOCKCHAIN = "changeBlockchain", + REQUEST_FEES = "requestFees", + REQUEST_LAST_BLOCK_INFO = "requestLastBlockInfo", + CHANGE_BLOCK_NOTIFICATION_SOUND_VOLUME = "changeBlockNotificationSoundVolume", + CHANGE_FEE_NOTIFICATION_SOUND_VOLUME = "changeFeeNotificationSoundVolume", + CHANGE_BLOCK_NOTIFICATION_SOUND = "changeBlockNotificationSound", + CHANGE_FEE_NOTIFICATION_SOUND = "changeFeeNotificationSound", + CHANGE_FEE_NOTIFICATION_BORDER = "changeFeeNotificationBorder", + CHANGE_FEE_NOTIFICATION_BORDER_CHANGE_STATE = "changeFeeNotificationBorderChangeState", + CHANGE_FEE_NOTIFICATION_ENABLED = "changeFeeNotificationEnabled" +} + +export enum OffscreenMessageType { + PLAY_BLOCK_NOTIFICATION_SOUND = "playBlockNotificationSound", + PLAY_FEE_NOTIFICATION_SOUND = "playFeeNotificationSound" +} + +export enum PopupMessageType { + FEES = "fees", + INITIAL_FEES = "initialFees", + BLOCK_INFO = "blockInfo", + INITIAL_BLOCK_INFO = "initialBlockInfo" +} diff --git a/src/models/messages/types.ts b/src/models/messages/types.ts new file mode 100644 index 0000000..d4656b0 --- /dev/null +++ b/src/models/messages/types.ts @@ -0,0 +1,115 @@ +import { BlockInfo } from "@models/block/types"; +import { FeeNotificationBorder, Fees } from "@models/fee/types"; +import { BackgroundMessageType, MessageTarget, OffscreenMessageType, PopupMessageType } from "@models/messages/enums"; + +export interface Message { + data?: Record; + target: MessageTarget[]; + type: string; +} + +export interface ChangeBlockNotificationEnabledBackgroundMessage extends Message { + data: { + enabled: boolean; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_ENABLED; +} + +export interface ChangeBlockchainBackgroundMessage extends Message { + data: { + isMainnet: boolean; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_BLOCKCHAIN; +} + +export interface RequestFeesBackgroundMessage extends Message { + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.REQUEST_FEES; +} + +export interface RequestLastBlockInfoBackgroundMessage extends Message { + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.REQUEST_LAST_BLOCK_INFO; +} + +export interface ChangeNotificationSoundVolumeBackgroundMessage extends Message { + data: { + volume: number; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND_VOLUME | BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND_VOLUME; +} + +export interface ChangeNotificationSoundBackgroundMessage extends Message { + data: { + sound: string | null; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND | BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND; +} + +export interface ChangeNotificationBorderBackgroundMessage extends Message { + data: { + feeBorder: FeeNotificationBorder | null; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER; +} + +export interface ChangeNotificationBorderChangeStateBackgroundMessage extends Message { + data: { + feeNotificationBorderChangeState: boolean; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER_CHANGE_STATE; +} + +export interface ChangeFeeNotificationEnabledBackgroundMessage extends Message { + data: { + enabled: boolean; + }; + target: [MessageTarget.BACKGROUND]; + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_ENABLED; +} + +export type BackgroundMessage = + | ChangeBlockNotificationEnabledBackgroundMessage + | ChangeBlockchainBackgroundMessage + | RequestFeesBackgroundMessage + | RequestLastBlockInfoBackgroundMessage + | ChangeNotificationSoundVolumeBackgroundMessage + | ChangeNotificationSoundBackgroundMessage + | ChangeNotificationBorderBackgroundMessage + | ChangeFeeNotificationEnabledBackgroundMessage + | ChangeNotificationBorderChangeStateBackgroundMessage; + +export interface PlayNotificationSoundOffscreenMessage extends Message { + data: { + volume: number; + sound: string | null; + }; + target: [MessageTarget.OFFSCREEN]; + type: OffscreenMessageType.PLAY_BLOCK_NOTIFICATION_SOUND | OffscreenMessageType.PLAY_FEE_NOTIFICATION_SOUND; +} + +export type OffscreenMessage = PlayNotificationSoundOffscreenMessage; + +export interface FeesPopupMessage extends Message { + data: { + fees: Fees | null; + }; + target: [MessageTarget.POPUP]; + type: PopupMessageType.FEES | PopupMessageType.INITIAL_FEES; +} + +export interface BlockPopupMessage extends Message { + data: { + blockInfo: BlockInfo; + }; + target: [MessageTarget.POPUP]; + type: PopupMessageType.BLOCK_INFO | PopupMessageType.INITIAL_BLOCK_INFO; +} + +export type PopupMessage = FeesPopupMessage | BlockPopupMessage; diff --git a/src/models/types.ts b/src/models/types.ts deleted file mode 100644 index 8774044..0000000 --- a/src/models/types.ts +++ /dev/null @@ -1,126 +0,0 @@ -export interface Message { - data?: Record; - target: string; - type: string; -} - -export interface ChangeBlockNotificationEnabledBackgroundMessage extends Message { - data: { - enabled: boolean; - }; - target: "background"; - type: "changeBlockNotificationEnabled"; -} - -export interface ChangeBlockchainBackgroundMessage extends Message { - data: { - isMainnet: boolean; - }; - target: "background"; - type: "changeBlockchain"; -} - -export interface RequestFeesBackgroundMessage extends Message { - target: "background"; - type: "requestFees"; -} - -export interface RequestLastBlockInfoBackgroundMessage extends Message { - target: "background"; - type: "requestLastBlockInfo"; -} - -export interface ChangeNotificationSoundVolumeBackgroundMessage extends Message { - data: { - volume: number; - }; - target: "background"; - type: "changeBlockNotificationSoundVolume" | "changeFeeNotificationSoundVolume"; -} - -export interface ChangeNotificationSoundBackgroundMessage extends Message { - data: { - sound: string | null; - }; - target: "background"; - type: "changeBlockNotificationSound" | "changeFeeNotificationSound"; -} - -export interface ChangeNotificationBorderBackgroundMessage extends Message { - data: { - feeBorder: FeeNotificationBorder | null; - }; - target: "background"; - type: "changeFeeNotificationBorder"; -} - -export interface ChangeFeeNotificationEnabledBackgroundMessage extends Message { - data: { - enabled: boolean; - }; - target: "background"; - type: "changeFeeNotificationEnabled"; -} - -export type BackgroundMessage = - | ChangeBlockNotificationEnabledBackgroundMessage - | ChangeBlockchainBackgroundMessage - | RequestFeesBackgroundMessage - | RequestLastBlockInfoBackgroundMessage - | ChangeNotificationSoundVolumeBackgroundMessage - | ChangeNotificationSoundBackgroundMessage - | ChangeNotificationBorderBackgroundMessage - | ChangeFeeNotificationEnabledBackgroundMessage; - -export interface PlayNotificationSoundOffscreenMessage extends Message { - data: { - volume: number; - sound: string | null; - }; - target: "offscreen"; - type: "playBlockNotificationSound" | "playFeeNotificationSound"; -} - -export type OffscreenMessage = PlayNotificationSoundOffscreenMessage; - -export interface FeesPopupMessage extends Message { - data: { - fees: Fees | null; - }; - target: "popup"; - type: "fees" | "initialFees"; -} - -export interface BlockPopupMessage extends Message { - data: { - blockInfo: BlockInfo; - }; - target: "popup"; - type: "blockInfo" | "initialBlockInfo"; -} - -export type PopupMessage = FeesPopupMessage | BlockPopupMessage; - -export interface Fees { - economyFee: number; - fastestFee: number; - halfHourFee: number; - hourFee: number; - minimumFee: number; -} - -export interface BlockInfo { - lastBlockHeight: number | null; - lastBlockTime: number | null; -} - -export enum FeeLevel { - SLOW = "hourFee", - MEDIUM = "halfHourFee", - FAST = "fastestFee" -} - -export interface FeeNotificationBorder { - feeBorder: number | null; - feeLevel: FeeLevel; -} diff --git a/src/offScreen/index.ts b/src/offScreen/index.ts index 21d1dce..1843087 100644 --- a/src/offScreen/index.ts +++ b/src/offScreen/index.ts @@ -1,15 +1,18 @@ -import { OffscreenMessage } from "@models/types"; +import { MessageTarget, OffscreenMessageType } from "@models/messages/enums"; +import { OffscreenMessage } from "@models/messages/types"; import blockNotification from "@static/sounds/blockNotification.m4a"; import feeNotification from "@static/sounds/feeNotification.wav"; console.debug("Initialize offscreen"); -function getDefaultSound(type: "playBlockNotificationSound" | "playFeeNotificationSound"): string { +function getDefaultSound( + type: OffscreenMessageType.PLAY_BLOCK_NOTIFICATION_SOUND | OffscreenMessageType.PLAY_FEE_NOTIFICATION_SOUND +): string { switch (type) { - case "playBlockNotificationSound": { + case OffscreenMessageType.PLAY_BLOCK_NOTIFICATION_SOUND: { return blockNotification; } - case "playFeeNotificationSound": { + case OffscreenMessageType.PLAY_FEE_NOTIFICATION_SOUND: { return feeNotification; } // skip default @@ -18,8 +21,9 @@ function getDefaultSound(type: "playBlockNotificationSound" | "playFeeNotificati chrome.runtime.onMessage.addListener((message: OffscreenMessage) => { if ( - message.target === "offscreen" && - (message.type === "playBlockNotificationSound" || message.type === "playFeeNotificationSound") + message.target.includes(MessageTarget.OFFSCREEN) && + (message.type === OffscreenMessageType.PLAY_BLOCK_NOTIFICATION_SOUND || + message.type === OffscreenMessageType.PLAY_FEE_NOTIFICATION_SOUND) ) { const sound = new Audio(message.data.sound ?? getDefaultSound(message.type)); diff --git a/src/popup/components/Main/BlockNotificationRadioButtonGroup.tsx b/src/popup/components/Main/BlockNotificationRadioButtonGroup.tsx index 0ef638b..c005a3f 100644 --- a/src/popup/components/Main/BlockNotificationRadioButtonGroup.tsx +++ b/src/popup/components/Main/BlockNotificationRadioButtonGroup.tsx @@ -1,4 +1,5 @@ import { useIsBlockNotificationEnabled } from "@context/MainContext"; +import { translate } from "@coreUtils/localeUtils"; import mainStyles from "./styles/Main.module.scss"; import styles from "./styles/RadioButtons.module.scss"; @@ -8,7 +9,7 @@ export default function BlockNotificationRadioButtonGroup() { return (
-

Blocks

+

{translate("blocks")}

setIsBlockNotificationEnabled(true)} checked={isBlockNotificationEnabled} /> - +
setIsBlockNotificationEnabled(false)} checked={!isBlockNotificationEnabled} /> - +
diff --git a/src/popup/components/Main/ChangeBlockchainRadioButtonGroup.tsx b/src/popup/components/Main/ChangeBlockchainRadioButtonGroup.tsx index fcc1624..19cb0a3 100644 --- a/src/popup/components/Main/ChangeBlockchainRadioButtonGroup.tsx +++ b/src/popup/components/Main/ChangeBlockchainRadioButtonGroup.tsx @@ -1,7 +1,9 @@ import { useEffect, useState } from "react"; -import { sendMessage } from "@coreUtils/utils"; -import { ChangeBlockchainBackgroundMessage } from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { ChangeBlockchainBackgroundMessage } from "@models/messages/types"; import mainStyles from "./styles/Main.module.scss"; import styles from "./styles/RadioButtons.module.scss"; @@ -12,11 +14,11 @@ export default function ChangeBlockchainRadioButtonGroup() { const changeBlockchain = (_isMainnet: boolean) => { setIsMainnet(_isMainnet); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { isMainnet: _isMainnet }, - type: "changeBlockchain" + type: BackgroundMessageType.CHANGE_BLOCKCHAIN }); chrome.storage.local.set({ isMainnet: _isMainnet }); }; @@ -31,7 +33,7 @@ export default function ChangeBlockchainRadioButtonGroup() { return (
-

Blockchain

+

{translate("blockchain")}

changeBlockchain(true)} checked={isMainnet} /> - +
changeBlockchain(false)} checked={!isMainnet} /> - +
diff --git a/src/popup/components/Main/FeeInfo.tsx b/src/popup/components/Main/FeeInfo.tsx index 8422440..7be8e03 100644 --- a/src/popup/components/Main/FeeInfo.tsx +++ b/src/popup/components/Main/FeeInfo.tsx @@ -1,8 +1,11 @@ import { useCallback, useEffect, useState } from "react"; import { useIsTrackingEnabled } from "@context/MainContext"; -import { sendMessage } from "@coreUtils//utils"; -import { Fees, FeesPopupMessage, PopupMessage, RequestFeesBackgroundMessage } from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { Fees } from "@models/fee/types"; +import { BackgroundMessageType, MessageTarget, PopupMessageType } from "@models/messages/enums"; +import { FeesPopupMessage, PopupMessage, RequestFeesBackgroundMessage } from "@models/messages/types"; import styles from "./styles/FeeInfo.module.scss"; import mainStyles from "./styles/Main.module.scss"; @@ -10,23 +13,25 @@ import mainStyles from "./styles/Main.module.scss"; export default function FeeInfo() { const isTrackingEnabled = useIsTrackingEnabled(); - const noDataText = isTrackingEnabled ? "Loading..." : "Tracking is disabled"; + const noDataText = isTrackingEnabled ? translate("loading") : translate("trackingIsDisabled"); const [fees, setFees] = useState(); const updateFees = useCallback((message: PopupMessage) => { - if (message.target === "popup" && message.type === "fees") { + if (message.target.includes(MessageTarget.POPUP) && message.type === PopupMessageType.FEES) { setFees(message.data.fees); } }, []); useEffect(() => { if (!fees) { - sendMessage({ - target: "background", - type: "requestFees" - }).then((message: FeesPopupMessage) => { - setFees(message.data.fees); + sendMessage({ + target: [MessageTarget.BACKGROUND], + type: BackgroundMessageType.REQUEST_FEES + }).then((message: FeesPopupMessage | void) => { + if (message) { + setFees(message.data.fees); + } }); } @@ -38,23 +43,23 @@ export default function FeeInfo() { return (
-

Fees info

+

{translate("feesInfo")}

{fees ? (
- Slow - Medium - Fast + {translate("slow")} + {translate("medium")} + {translate("fast")}
{fees.hourFee} - sat/vB + {translate("satPerVb")}
{fees.halfHourFee} - sat/vB + {translate("satPerVb")}
{fees.fastestFee} - sat/vB + {translate("satPerVb")}
) : ( diff --git a/src/popup/components/Main/FeeNotificationRadioButtonGroup.tsx b/src/popup/components/Main/FeeNotificationRadioButtonGroup.tsx index ab47533..935ee0d 100644 --- a/src/popup/components/Main/FeeNotificationRadioButtonGroup.tsx +++ b/src/popup/components/Main/FeeNotificationRadioButtonGroup.tsx @@ -1,4 +1,5 @@ import { useIsFeeNotificationEnabled } from "@context/MainContext"; +import { translate } from "@coreUtils/localeUtils"; import mainStyles from "./styles/Main.module.scss"; import styles from "./styles/RadioButtons.module.scss"; @@ -8,7 +9,7 @@ export default function FeeNotificationRadioButtonGroup() { return (
-

Fee

+

{translate("fee")}

setIsFeeNotificationEnabled(true)} checked={isFeeNotificationEnabled} /> - +
setIsFeeNotificationEnabled(false)} checked={!isFeeNotificationEnabled} /> - +
diff --git a/src/popup/components/Main/LastBlockInfo.tsx b/src/popup/components/Main/LastBlockInfo.tsx index 91217ff..dfa345c 100644 --- a/src/popup/components/Main/LastBlockInfo.tsx +++ b/src/popup/components/Main/LastBlockInfo.tsx @@ -1,16 +1,36 @@ import { useCallback, useEffect, useState } from "react"; import { useIsTrackingEnabled } from "@context/MainContext"; -import { sendMessage } from "@coreUtils/utils"; -import { BlockInfo, BlockPopupMessage, PopupMessage, RequestLastBlockInfoBackgroundMessage } from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { BlockInfo } from "@models/block/types"; +import { BackgroundMessageType, MessageTarget, PopupMessageType } from "@models/messages/enums"; +import { BlockPopupMessage, PopupMessage, RequestLastBlockInfoBackgroundMessage } from "@models/messages/types"; import styles from "./styles/LastBlockInfo.module.scss"; import mainStyles from "./styles/Main.module.scss"; +const uiLang = chrome.i18n.getUILanguage(); + +const ruRules = new Intl.PluralRules("ru-RU"); +const enRules = new Intl.PluralRules("en-US"); + +const minutesSuffixes = new Map([ + ["one", translate("oneMinuteAgo")], + ["few", translate("fewMinutesAgo")], + ["many", translate("manyMinutesAgo")] +]); + +export const formatMinutesCount = (minutes: number) => { + const rule = uiLang === "ru" ? ruRules.select(minutes) : enRules.select(minutes); + + return `${minutes} ${minutesSuffixes.get(rule)}`; +}; + export default function LastBlockInfo() { const isTrackingEnabled = useIsTrackingEnabled(); - const noDataText = isTrackingEnabled ? "Loading..." : "Tracking is disabled"; + const noDataText = isTrackingEnabled ? translate("loading") : translate("trackingIsDisabled"); const [lastBlockInfo, setLastBlockInfo] = useState(); const [currentDate, setCurrentDate] = useState(Date.now()); @@ -18,7 +38,7 @@ export default function LastBlockInfo() { const blockMinedAgoSeconds = (lastBlockTime: number) => Math.round(Math.abs(currentDate - lastBlockTime) / 1000); const updateLastBlockInfo = useCallback((message: PopupMessage) => { - if (message.target === "popup" && message.type === "blockInfo") { + if (message.target.includes(MessageTarget.POPUP) && message.type === PopupMessageType.BLOCK_INFO) { setLastBlockInfo(message.data.blockInfo); } }, []); @@ -31,11 +51,13 @@ export default function LastBlockInfo() { useEffect(() => { if (!lastBlockInfo) { - sendMessage({ - target: "background", - type: "requestLastBlockInfo" - }).then((message: BlockPopupMessage) => { - setLastBlockInfo(message.data.blockInfo); + sendMessage({ + target: [MessageTarget.BACKGROUND], + type: BackgroundMessageType.REQUEST_LAST_BLOCK_INFO + }).then((message: BlockPopupMessage | void) => { + if (message) { + setLastBlockInfo(message.data.blockInfo); + } }); } @@ -49,18 +71,18 @@ export default function LastBlockInfo() { return (
-

Last block info

+

{translate("lastBlockInfo")}

{lastBlockInfo?.lastBlockTime && lastBlockInfo?.lastBlockHeight ? (
- Last block height: + {translate("lastBlockHeight")} {lastBlockInfo.lastBlockHeight}
- Last block mined: + {translate("lastBlockMined")} {blockMinedAgoSeconds(lastBlockInfo.lastBlockTime) > 60 - ? `${Math.round(blockMinedAgoSeconds(lastBlockInfo.lastBlockTime) / 60)} minutes ago` + ? formatMinutesCount(Math.round(blockMinedAgoSeconds(lastBlockInfo.lastBlockTime) / 60)) : "< minute ago"}{" "}
diff --git a/src/popup/components/Main/index.tsx b/src/popup/components/Main/index.tsx index 9a28e45..b7375af 100644 --- a/src/popup/components/Main/index.tsx +++ b/src/popup/components/Main/index.tsx @@ -1,3 +1,4 @@ +import { translate } from "@coreUtils/localeUtils"; import FeeNotificationRadioButtonGroup from "@main/FeeNotificationRadioButtonGroup"; import BlockNotificationRadioButtonGroup from "./BlockNotificationRadioButtonGroup"; @@ -10,7 +11,7 @@ export default function Main() { return ( <>
-

Tracking state

+

{translate("trackingState")}

diff --git a/src/popup/components/Main/styles/FeeInfo.module.scss b/src/popup/components/Main/styles/FeeInfo.module.scss index c3b4806..695a527 100644 --- a/src/popup/components/Main/styles/FeeInfo.module.scss +++ b/src/popup/components/Main/styles/FeeInfo.module.scss @@ -1,6 +1,6 @@ .container { display: grid; - gap: 10px 10px; + gap: 10px; grid-template-columns: repeat(3, 70px); } @@ -25,4 +25,4 @@ .loading { color: var(#fff6); font-size: 18px; -} \ No newline at end of file +} diff --git a/src/popup/components/Main/styles/LastBlockInfo.module.scss b/src/popup/components/Main/styles/LastBlockInfo.module.scss index be104d9..f606c77 100644 --- a/src/popup/components/Main/styles/LastBlockInfo.module.scss +++ b/src/popup/components/Main/styles/LastBlockInfo.module.scss @@ -19,4 +19,4 @@ .loading { color: var(#fff6); font-size: 18px; -} \ No newline at end of file +} diff --git a/src/popup/components/Main/styles/RadioButtons.module.scss b/src/popup/components/Main/styles/RadioButtons.module.scss index bdcf1f4..11f74c4 100644 --- a/src/popup/components/Main/styles/RadioButtons.module.scss +++ b/src/popup/components/Main/styles/RadioButtons.module.scss @@ -46,4 +46,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/popup/components/Settings/SettingsTabs/BlockNotificationTab/index.tsx b/src/popup/components/Settings/SettingsTabs/BlockNotificationTab/index.tsx index c5d2d80..7f27c30 100644 --- a/src/popup/components/Settings/SettingsTabs/BlockNotificationTab/index.tsx +++ b/src/popup/components/Settings/SettingsTabs/BlockNotificationTab/index.tsx @@ -1,5 +1,7 @@ import { useState } from "react"; +import { translate } from "@coreUtils/localeUtils"; +import { BackgroundMessageType } from "@models/messages/enums"; import SoundSelector from "@settings/SettingsTabs/SoundSelector"; import VolumeControl from "@settings/SettingsTabs/VolumeControl"; import blockNotification from "@static/sounds/blockNotification.m4a"; @@ -11,17 +13,18 @@ export default function BlockNotificationTab() { return (
+ {translate("blockNotificationSettingsDescription")} diff --git a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderCheckbox.tsx b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderCheckbox.tsx new file mode 100644 index 0000000..35cd0d7 --- /dev/null +++ b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderCheckbox.tsx @@ -0,0 +1,47 @@ +import { ChangeEvent, useCallback, useEffect, useState } from "react"; + +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { ChangeNotificationBorderChangeStateBackgroundMessage } from "@models/messages/types"; + +import mainStyles from "../styles/SettingsTabs.module.scss"; +import styles from "./styles/FeeBorderCheckbox.module.scss"; + +export default function FeeBorderCheckbox() { + const [feeBorderChangeState, setFeeBorderChangeState] = useState(false); + + const onFeeBorderChangeStateChanged = useCallback((event: ChangeEvent) => { + const newFeeBorderChangeState = (event.target as HTMLInputElement).checked; + + setFeeBorderChangeState(newFeeBorderChangeState); + chrome.storage.local.set({ feeNotificationBorderChangeState: newFeeBorderChangeState }); + sendMessage({ + target: [MessageTarget.BACKGROUND], + data: { feeNotificationBorderChangeState: newFeeBorderChangeState }, + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER_CHANGE_STATE + }); + }, []); + + useEffect(() => { + chrome.storage.local.get(["feeNotificationBorderChangeState"]).then((result) => { + const storageFeeNotificationBorderChangeState = result.feeNotificationBorderChangeState; + const storageFeeBorderChangeState = storageFeeNotificationBorderChangeState ?? false; + setFeeBorderChangeState(storageFeeBorderChangeState); + }); + }, []); + + return ( +
+ + +
+ ); +} diff --git a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderInput.tsx b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderInput.tsx index a4caf01..9e9ee0d 100644 --- a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderInput.tsx +++ b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/FeeBorderInput.tsx @@ -1,8 +1,11 @@ import { FormEvent, useCallback, useEffect, useState } from "react"; import { useIsAutoFeeNotificationEnabled, useIsFeeNotificationEnabled } from "@context/MainContext"; -import { sendMessage } from "@coreUtils/utils"; -import { ChangeNotificationBorderBackgroundMessage, FeeLevel } from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { FeeLevel } from "@models/fee/types"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { ChangeNotificationBorderBackgroundMessage } from "@models/messages/types"; import Icon from "@parts/Icon"; import clearIcon from "@static/icons/clear.svg"; import saveIcon from "@static/icons/save.svg"; @@ -43,9 +46,9 @@ export default function FeeBorderInput() { const _feeBorder = feeBorder === undefined ? null : feeBorder; chrome.storage.local.set({ feeNotificationBorder: { feeBorder: _feeBorder, feeLevel } }); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { feeBorder: { feeBorder: _feeBorder, feeLevel } }, - type: "changeFeeNotificationBorder" + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_BORDER }); setInitialFeeBorder(feeBorder); setInitialFeeLevel(feeLevel); @@ -85,22 +88,22 @@ export default function FeeBorderInput() { return (
diff --git a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/index.tsx b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/index.tsx index bccde8c..1590353 100644 --- a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/index.tsx +++ b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/index.tsx @@ -1,5 +1,8 @@ import { useState } from "react"; +import { translate } from "@coreUtils/localeUtils"; +import { BackgroundMessageType } from "@models/messages/enums"; +import FeeBorderCheckbox from "@settings/SettingsTabs/FeeNotificationTab/FeeBorderCheckbox"; import FeeBorderInput from "@settings/SettingsTabs/FeeNotificationTab/FeeBorderInput"; import SoundSelector from "@settings/SettingsTabs/SoundSelector"; import VolumeControl from "@settings/SettingsTabs/VolumeControl"; @@ -12,18 +15,20 @@ export default function FeeNotificationTab() { return (
+ {translate("feesNotificationSettingsDescription")} + diff --git a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderCheckbox.module.scss b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderCheckbox.module.scss new file mode 100644 index 0000000..60e71e6 --- /dev/null +++ b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderCheckbox.module.scss @@ -0,0 +1,8 @@ +.container { + align-items: center; + column-gap: 7px; + display: flex; + flex-wrap: nowrap; + justify-content: center; + width: 100%; +} diff --git a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderInput.module.scss b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderInput.module.scss index 02a6887..497a56f 100644 --- a/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderInput.module.scss +++ b/src/popup/components/Settings/SettingsTabs/FeeNotificationTab/styles/FeeBorderInput.module.scss @@ -15,4 +15,4 @@ .error-message { color: #af000e; font-weight: bold; -} \ No newline at end of file +} diff --git a/src/popup/components/Settings/SettingsTabs/SoundSelector.tsx b/src/popup/components/Settings/SettingsTabs/SoundSelector.tsx index f0cc3f2..1aefff3 100644 --- a/src/popup/components/Settings/SettingsTabs/SoundSelector.tsx +++ b/src/popup/components/Settings/SettingsTabs/SoundSelector.tsx @@ -1,7 +1,9 @@ import { FormEvent, useCallback, useEffect, useState } from "react"; -import { sendMessage } from "@coreUtils/utils"; -import { ChangeNotificationSoundBackgroundMessage } from "@models/types"; +import { translate } from "@coreUtils/localeUtils"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { ChangeNotificationSoundBackgroundMessage } from "@models/messages/types"; import Icon from "@parts/Icon"; import clearIcon from "@static/icons/clear.svg"; import playIcon from "@static/icons/play.svg"; @@ -12,7 +14,7 @@ import styles from "./styles/SoundSelector.module.scss"; interface SoundSelectorProps { label: string; - eventType: "changeBlockNotificationSound" | "changeFeeNotificationSound"; + eventType: BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND | BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND; storageKey: "blockNotificationSound" | "feeNotificationSound"; defaultSound: string; volume: number; @@ -46,7 +48,7 @@ export default function SoundSelector({ label, eventType, storageKey, defaultSou return true; } catch (error) { console.warn((error as Error).message); - setErrorMessage("Could not use this sound: incorrect format"); + setErrorMessage(translate("soundSelectorIncorrectFormatError")); return false; } } @@ -61,7 +63,7 @@ export default function SoundSelector({ label, eventType, storageKey, defaultSou if (result) { chrome.storage.local.set({ [storageKey]: _sound }); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { sound: _sound }, type: eventType }); @@ -88,7 +90,7 @@ export default function SoundSelector({ label, eventType, storageKey, defaultSou
diff --git a/src/popup/components/Settings/SettingsTabs/VolumeControl.tsx b/src/popup/components/Settings/SettingsTabs/VolumeControl.tsx index b1aaa64..4756642 100644 --- a/src/popup/components/Settings/SettingsTabs/VolumeControl.tsx +++ b/src/popup/components/Settings/SettingsTabs/VolumeControl.tsx @@ -1,14 +1,17 @@ import React, { FormEvent, useCallback, useEffect } from "react"; -import { sendMessage } from "@coreUtils/utils"; -import { ChangeNotificationSoundVolumeBackgroundMessage } from "@models/types"; +import { sendMessage } from "@coreUtils/messagesUtils"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { ChangeNotificationSoundVolumeBackgroundMessage } from "@models/messages/types"; import mainStyles from "./styles/SettingsTabs.module.scss"; import styles from "./styles/VolumeControl.module.scss"; interface VolumeControlProps { label: string; - eventType: "changeBlockNotificationSoundVolume" | "changeFeeNotificationSoundVolume"; + eventType: + | BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_SOUND_VOLUME + | BackgroundMessageType.CHANGE_FEE_NOTIFICATION_SOUND_VOLUME; storageKey: "blockNotificationVolume" | "feeNotificationVolume"; volume: number; setVolume: React.Dispatch>; @@ -31,7 +34,7 @@ export default function VolumeControl({ label, eventType, storageKey, volume, se setVolume(newVolume); chrome.storage.local.set({ [storageKey]: newVolume }); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { volume: newVolume }, type: eventType }); diff --git a/src/popup/components/Settings/SettingsTabs/index.tsx b/src/popup/components/Settings/SettingsTabs/index.tsx index 8b8e193..8c292f6 100644 --- a/src/popup/components/Settings/SettingsTabs/index.tsx +++ b/src/popup/components/Settings/SettingsTabs/index.tsx @@ -1,6 +1,7 @@ import { useEffect } from "react"; import { useActiveSettingsTab } from "@context/MainContext"; +import { translate } from "@coreUtils/localeUtils"; import TabItem from "@parts/Tabs/TabItem"; import Tabs from "@parts/Tabs/Tabs"; import BlockNotificationTab from "@settings/SettingsTabs/BlockNotificationTab"; @@ -18,12 +19,12 @@ export default function SettingsTabs() { return (
-

Notification settings

+

{translate("notificationSettings")}

- + - + diff --git a/src/popup/components/Settings/SettingsTabs/styles/SoundSelector.module.scss b/src/popup/components/Settings/SettingsTabs/styles/SoundSelector.module.scss index 02a6887..497a56f 100644 --- a/src/popup/components/Settings/SettingsTabs/styles/SoundSelector.module.scss +++ b/src/popup/components/Settings/SettingsTabs/styles/SoundSelector.module.scss @@ -15,4 +15,4 @@ .error-message { color: #af000e; font-weight: bold; -} \ No newline at end of file +} diff --git a/src/popup/components/Settings/SettingsTabs/styles/VolumeControl.module.scss b/src/popup/components/Settings/SettingsTabs/styles/VolumeControl.module.scss index 2ab3f06..93fe9e3 100644 --- a/src/popup/components/Settings/SettingsTabs/styles/VolumeControl.module.scss +++ b/src/popup/components/Settings/SettingsTabs/styles/VolumeControl.module.scss @@ -1,4 +1,4 @@ .control-container { column-gap: 10px; display: flex; -} \ No newline at end of file +} diff --git a/src/popup/context/WithMainContext.tsx b/src/popup/context/WithMainContext.tsx index ca1a904..06ecc91 100644 --- a/src/popup/context/WithMainContext.tsx +++ b/src/popup/context/WithMainContext.tsx @@ -1,8 +1,12 @@ import { PropsWithChildren, useCallback, useEffect, useMemo, useState } from "react"; -import { sendMessage } from "@coreUtils/utils"; +import { sendMessage } from "@coreUtils/messagesUtils"; import { useToggle } from "@hooks/useToogle"; -import { ChangeBlockNotificationEnabledBackgroundMessage, ChangeFeeNotificationEnabledBackgroundMessage } from "@models/types"; +import { BackgroundMessageType, MessageTarget } from "@models/messages/enums"; +import { + ChangeBlockNotificationEnabledBackgroundMessage, + ChangeFeeNotificationEnabledBackgroundMessage +} from "@models/messages/types"; import { MainContext } from "./MainContext"; @@ -17,9 +21,9 @@ export default function WithMainContext({ children }: PropsWithChildren) { setIsBlockNotificationEnabled(blockNotificationState); chrome.storage.local.set({ isBlockNotificationEnabled: blockNotificationState }); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { enabled: blockNotificationState }, - type: "changeBlockNotificationEnabled" + type: BackgroundMessageType.CHANGE_BLOCK_NOTIFICATION_ENABLED }); }, []); @@ -27,9 +31,9 @@ export default function WithMainContext({ children }: PropsWithChildren) { setIsFeeNotificationEnabled(feeNotificationState); chrome.storage.local.set({ isFeeNotificationEnabled: feeNotificationState }); sendMessage({ - target: "background", + target: [MessageTarget.BACKGROUND], data: { enabled: feeNotificationState }, - type: "changeFeeNotificationEnabled" + type: BackgroundMessageType.CHANGE_FEE_NOTIFICATION_ENABLED }); }, []); diff --git a/src/popup/styles/Popup.module.scss b/src/popup/styles/Popup.module.scss index 4f57878..b3cd702 100644 --- a/src/popup/styles/Popup.module.scss +++ b/src/popup/styles/Popup.module.scss @@ -6,5 +6,5 @@ min-height: 410px; row-gap: 20px; text-align: center; - width: 250px; + width: 270px; } diff --git a/src/utils/localeUtils.ts b/src/utils/localeUtils.ts new file mode 100644 index 0000000..bdf1928 --- /dev/null +++ b/src/utils/localeUtils.ts @@ -0,0 +1 @@ +export const translate = (name: string) => chrome.i18n.getMessage(name); diff --git a/src/utils/utils.ts b/src/utils/messagesUtils.ts similarity index 73% rename from src/utils/utils.ts rename to src/utils/messagesUtils.ts index d960702..dc6eff8 100644 --- a/src/utils/utils.ts +++ b/src/utils/messagesUtils.ts @@ -1,8 +1,8 @@ -import { Message } from "@models/types"; +import { Message } from "@models/messages/types"; -export async function sendMessage(message: T): Promise { +export async function sendMessage(message: T): Promise { try { - return await chrome.runtime.sendMessage(message); + return await chrome.runtime.sendMessage(message); } catch (error) { if (error instanceof Error) { if (error.message === "Could not establish connection. Receiving end does not exist.") { diff --git a/webpack.config.ts b/webpack.config.ts index 907dd36..91c2f86 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -16,6 +16,7 @@ export const PATHS = { publicFiles: path.join(__dirname, "./public/files"), publicIcons: path.join(__dirname, "./public/icons"), publicImages: path.join(__dirname, "./public/images"), + publicLocales: path.join(__dirname, "./public/_locales"), dist: path.join(__dirname, "./dist"), distJs: path.join(__dirname, "./dist/js"), distIcons: path.join(__dirname, "./dist/icons"), @@ -142,6 +143,10 @@ const webpack_ = (_: any, argv: any) => { { from: `${PATHS.publicIcons}/`, to: "../icons/" + }, + { + from: `${PATHS.publicLocales}/`, + to: "../_locales/" } ] }), From 7510c67efb3e364592f0824a745680ff4416c082 Mon Sep 17 00:00:00 2001 From: ivansavoskin Date: Tue, 6 Aug 2024 20:57:28 +0300 Subject: [PATCH 2/2] Update build action --- .github/workflows/build.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c7c991..f81b5bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,6 @@ name: build -on: - push: - branches: [ master, develop ] - pull_request: - branches: [ master, develop ] +on: [pull_request] jobs: build: