diff --git a/.distignore b/.distignore index f0b0e6a1..923ad27c 100644 --- a/.distignore +++ b/.distignore @@ -30,6 +30,7 @@ tailwind.config.js /.wordpress-org /.idea /src +/patches /bin /tests /phpunit.xml diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 02a91d2d..00000000 --- a/.eslintrc +++ /dev/null @@ -1,49 +0,0 @@ -{ - "env": { - "browser": true, - }, - "extends": [ "plugin:@wordpress/eslint-plugin/recommended" ], - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "ignorePatterns": [ "node_modules", "assets" ], - "rules": { - "linebreak-style": [ - "error", - "unix" - ], - "array-bracket-spacing": [ - "warn", - "always", - { - "arraysInArrays": false, - "objectsInArrays": false - } - ], - "key-spacing": [ - "warn", - { - "beforeColon": false, - "afterColon": true - } - ], - "object-curly-spacing": [ - "warn", - "always", - { - "arraysInObjects": true, - "objectsInObjects": false - } - ], - "@wordpress/i18n-text-domain": [ - "error", - { - "allowedTextDomain": "hyve-lite" - } - ] - } -} diff --git a/.github/workflows/create-buld-url.yml b/.github/workflows/create-buld-url.yml index d5d50e2e..c56ec4ed 100644 --- a/.github/workflows/create-buld-url.yml +++ b/.github/workflows/create-buld-url.yml @@ -25,6 +25,12 @@ jobs: run: composer install --no-dev --prefer-dist --no-progress --no-suggest - name: Install npm deps run: npm ci + - name: Bump the plugin version + run: | + CURRENT_VERSION=$(node -p -e "require('./package.json').version") + COMMIT_HASH=$(git rev-parse --short HEAD) + DEV_VERSION="${CURRENT_VERSION}-dev.${COMMIT_HASH}" + grunt version::${DEV_VERSION} - name: Build files run: npm run build - name: Create zip diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml new file mode 100644 index 00000000..2f455038 --- /dev/null +++ b/.github/workflows/translations.yml @@ -0,0 +1,48 @@ +name: Translations Diff + +on: + pull_request_review: + pull_request: + types: [opened, edited, synchronize, ready_for_review] + branches: + - development + - main + +jobs: + translation: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout Base Branch + uses: actions/checkout@v4 + with: + ref: ${{ github.base_ref }} + path: hyve-lite-base + - name: Setup node 22 + uses: actions/setup-node@v6 + with: + node-version: 22.x + - name: Checkout PR Branch (Head) + uses: actions/checkout@v4 + with: + path: hyve-lite-head + - name: Build POT for PR Branch + run: | + chmod +x ./hyve-lite-head/bin/make-pot.sh + ./hyve-lite-head/bin/make-pot.sh ./hyve-lite-head ./hyve-lite-head/languages/hyve-lite.pot + ls ./hyve-lite-head/languages/ + - name: Build POT for Base Branch + run: | + ./hyve-lite-head/bin/make-pot.sh ./hyve-lite-base ./hyve-lite-base/languages/hyve-lite.pot + ls ./hyve-lite-base/languages/ + - name: Compare POT files + uses: Codeinwp/action-i18n-string-reviewer@main + with: + fail-on-changes: "false" + openrouter-key: ${{ secrets.OPEN_ROUTER_API_KEY }} + openrouter-model: "google/gemini-2.5-flash" + base-pot-file: "hyve-lite-base/languages/hyve-lite.pot" + target-pot-file: "hyve-lite-head/languages/hyve-lite.pot" + github-token: ${{ secrets.BOT_TOKEN }} diff --git a/AGENTS.md b/AGENTS.md index b052401c..f2c3aecf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,7 +79,7 @@ Plugin constants are defined in `hyve-lite.php`: `HYVE_LITE_BASEFILE`, `HYVE_LIT Four separate webpack entry points built with `@wordpress/scripts`: -1. **src/backend/** — Admin dashboard React app using `@wordpress/element` and `@wordpress/data` for state management. Components in `components/`, page sections in `parts/`. +1. **src/backend/** — Admin dashboard React app using `@wordpress/element` and `@wordpress/data` for state management. Four tab screens in `screens/` (Dashboard, KnowledgeBase, Messages, Settings), shared layout kit in `components/`, hooks in `data/`, URL routing in `router.js` (`?page=hyve&nav=&sub=&item=`), shared store in `store.js`. Pro extends it through the `hyve.routes` / `hyve.slot` / `hyve.setup-steps` filters and the layout kit bridged at `window.hyveComponents.ui`. 2. **src/frontend/** — Client-facing chat widget. Vanilla JS class (`App`) — no React. Manages chat state, threads, audio, and localStorage persistence. 3. **src/block/** — Gutenberg block with two variations: inline and floating. Server-rendered via `render.php`. 4. **src/addons/** — Post list table row actions for quick knowledge base add/remove. diff --git a/Gruntfile.js b/Gruntfile.js index 027f21a5..0d92b313 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,44 +2,36 @@ /* jshint node:true */ /* global require */ -module.exports = function( grunt ) { +module.exports = function ( grunt ) { grunt.loadNpmTasks( 'grunt-version' ); - grunt.initConfig( - { - version: { - project: { - src: [ - 'package.json' - ] + grunt.initConfig( { + version: { + project: { + src: [ 'package.json' ], + }, + composer: { + src: [ 'composer.json' ], + }, + metatag: { + options: { + prefix: 'Version:\\s*', + flags: '', }, - composer: { - src: [ - 'composer.json' - ] + src: [ 'hyve-lite.php' ], + }, + php: { + options: { + prefix: "HYVE_LITE_VERSION', '", + flags: '', }, - metatag: { - options: { - prefix: 'Version:\\s*', - flags: '' - }, - src: [ 'hyve-lite.php' ] + src: [ 'hyve-lite.php' ], + }, + readmetxt: { + options: { + prefix: 'Stable tag:\\s*', }, - php: { - options: { - prefix: 'HYVE_LITE_VERSION\', \'', - flags: '' - }, - src: [ 'hyve-lite.php' ] - }, - readmetxt: { - options: { - prefix: 'Stable tag:\\s*' - }, - src: [ - 'readme.txt' - ] - } - } - } - ); + src: [ 'readme.txt' ], + }, + }, + } ); }; diff --git a/assets/audio/click.mp3 b/assets/audio/click.mp3 deleted file mode 100644 index 0ab81757..00000000 Binary files a/assets/audio/click.mp3 and /dev/null differ diff --git a/assets/images/icon.png b/assets/images/icon.png new file mode 100644 index 00000000..745ec9d5 Binary files /dev/null and b/assets/images/icon.png differ diff --git a/assets/images/threads.png b/assets/images/threads.png deleted file mode 100644 index 5d9cf240..00000000 Binary files a/assets/images/threads.png and /dev/null differ diff --git a/bin/make-pot.sh b/bin/make-pot.sh new file mode 100755 index 00000000..50f593fd --- /dev/null +++ b/bin/make-pot.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Script to generate POT file via Docker +# Usage: ./bin/make-pot.sh [plugin-path] [destination-path] + +# Set defaults +PLUGIN_PATH="${1:-.}" +DESTINATION="${2:-.}" + +# Resolve absolute paths +PLUGIN_PATH="$(cd "$PLUGIN_PATH" 2>/dev/null && pwd)" || { + echo "Error: Plugin path '$1' does not exist" + exit 1 +} + +DESTINATION="$(cd "$(dirname "$DESTINATION")" 2>/dev/null && pwd)/$(basename "$DESTINATION")" || { + echo "Error: Unable to resolve destination path" + exit 1 +} + +# Extract destination filename and directory +DEST_DIR="$(dirname "$DESTINATION")" +DEST_FILE="$(basename "$DESTINATION")" + +# Ensure destination directory exists +mkdir -p "$DEST_DIR" + +echo "Generating POT file..." +echo "Plugin Path: $PLUGIN_PATH" +echo "Destination: $DESTINATION" +echo "" + +# Run Docker container with wp-cli to generate POT +docker run --user root --rm \ + --volume "$PLUGIN_PATH:/var/www/html/plugin" \ + wordpress:cli \ + bash -c 'php -d memory_limit=512M "$(which wp)" --version --allow-root && wp i18n make-pot plugin ./plugin/languages/'"$DEST_FILE"' --include=inc,build,src --allow-root --domain=hyve-lite' + +# Check if the file was created inside the container +if [ $? -eq 0 ]; then + echo "" + echo "✓ POT file successfully generated at: $DESTINATION" +else + echo "" + echo "✗ Error generating POT file" + exit 1 +fi diff --git a/bin/set-settings.php b/bin/set-settings.php index e6516d49..4172394a 100644 --- a/bin/set-settings.php +++ b/bin/set-settings.php @@ -8,22 +8,7 @@ 'chat_enabled' => false, 'welcome_message' => 'Hello! How can I help you today?', 'default_message' => 'Sorry, I\'m not able to help with that.', - 'chat_model' => 'gpt-4o-mini', - 'temperature' => 1, - 'top_p' => 1, - 'moderation_threshold' => [ - 'sexual' => 80, - 'hate' => 70, - 'harassment' => 70, - 'self-harm' => 50, - 'sexual/minors' => 50, - 'hate/threatening' => 60, - 'violence/graphic' => 80, - 'self-harm/intent' => 50, - 'self-harm/instructions' => 50, - 'harassment/threatening' => 60, - 'violence' => 70, - ], + 'chat_model' => 'gpt-5.4-nano', ]; update_option( 'hyve_settings', $settings ); diff --git a/composer.json b/composer.json index d73c334c..83d0c502 100644 --- a/composer.json +++ b/composer.json @@ -43,11 +43,20 @@ }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, - "php-http/discovery": true + "php-http/discovery": true, + "cweagans/composer-patches": true } }, + "extra": { + "patches": { + "your1/qdrant": { + "Add #[\\ReturnTypeWillChange] to Response::offsetGet() for PHP 8.1+ compatibility": "patches/qdrant-response-returntypewillchange.patch" + } + } + }, "require": { "codeinwp/themeisle-sdk": "^3.3", + "cweagans/composer-patches": "^1.7", "guttedgarden/tiktoken": "^1.2", "your1/qdrant": "^0.5.3" } diff --git a/composer.lock b/composer.lock index 2cba8d17..eb2deaa5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "24efb89a5c7a992478f35e37acb6fd80", + "content-hash": "da3e0a9003acbadb89175339f2c6215d", "packages": [ { "name": "codeinwp/themeisle-sdk", - "version": "3.3.51", + "version": "3.3.57", "source": { "type": "git", "url": "https://github.com/Codeinwp/themeisle-sdk.git", - "reference": "bb2a8414b0418b18c68c9ff1df3d7fb10467928d" + "reference": "3c761b0bddda8d5963a47d14a40811869131030b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/bb2a8414b0418b18c68c9ff1df3d7fb10467928d", - "reference": "bb2a8414b0418b18c68c9ff1df3d7fb10467928d", + "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/3c761b0bddda8d5963a47d14a40811869131030b", + "reference": "3c761b0bddda8d5963a47d14a40811869131030b", "shasum": "" }, "require-dev": { @@ -36,16 +36,64 @@ "homepage": "https://themeisle.com" } ], - "description": "Themeisle SDK.", + "description": "Themeisle SDK library.", "homepage": "https://github.com/Codeinwp/themeisle-sdk", "keywords": [ "wordpress" ], "support": { "issues": "https://github.com/Codeinwp/themeisle-sdk/issues", - "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.51" + "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.57" }, - "time": "2026-03-30T07:58:49+00:00" + "time": "2026-07-23T13:31:25+00:00" + }, + { + "name": "cweagans/composer-patches", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/cweagans/composer-patches.git", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3.0" + }, + "require-dev": { + "composer/composer": "~1.0 || ~2.0", + "phpunit/phpunit": "~4.6" + }, + "type": "composer-plugin", + "extra": { + "class": "cweagans\\Composer\\Patches" + }, + "autoload": { + "psr-4": { + "cweagans\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Cameron Eagans", + "email": "me@cweagans.net" + } + ], + "description": "Provides a way to patch Composer packages.", + "support": { + "issues": "https://github.com/cweagans/composer-patches/issues", + "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + }, + "time": "2022-12-20T22:53:13+00:00" }, { "name": "guttedgarden/tiktoken", @@ -905,29 +953,29 @@ }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.1.2", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1" + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", - "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", "shasum": "" }, "require": { "composer-plugin-api": "^2.2", "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "^2.2", "ext-json": "*", "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", @@ -997,7 +1045,7 @@ "type": "thanks_dev" } ], - "time": "2025-07-17T20:45:56+00:00" + "time": "2026-05-06T08:26:05+00:00" }, { "name": "doctrine/instantiator", @@ -1307,16 +1355,16 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.8.2", + "version": "v6.9.4", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8" + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8", - "reference": "9c8e22e437463197c1ec0d5eaa9ddd4a0eb6d7f8", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5", + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5", "shasum": "" }, "conflict": { @@ -1326,10 +1374,11 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^5.5", "php": "^7.4 || ^8.0", - "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.4.1", + "php-stubs/generator": "^0.8.6", + "phpdocumentor/reflection-docblock": "^6.0", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", + "symfony/polyfill-php80": "*", "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" }, @@ -1352,9 +1401,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.2" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4" }, - "time": "2025-07-16T06:41:00+00:00" + "time": "2026-05-01T20:36:01+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1420,27 +1469,27 @@ }, { "name": "phpcsstandards/phpcsextra", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "882b8c947ada27eb002870fe77fee9ce0a454cdb" + "reference": "b598aa890815b8df16363271b659d73280129101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/882b8c947ada27eb002870fe77fee9ce0a454cdb", - "reference": "882b8c947ada27eb002870fe77fee9ce0a454cdb", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101", + "reference": "b598aa890815b8df16363271b659d73280129101", "shasum": "" }, "require": { "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.1.2", - "squizlabs/php_codesniffer": "^3.13.4 || ^4.0" + "phpcsstandards/phpcsutils": "^1.2.0", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevcs": "^1.2.0", "phpcsstandards/phpcsdevtools": "^1.2.1", "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, @@ -1498,32 +1547,32 @@ "type": "thanks_dev" } ], - "time": "2025-09-05T06:54:52+00:00" + "time": "2025-11-12T23:06:57+00:00" }, { "name": "phpcsstandards/phpcsutils", - "version": "1.1.2", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "b22b59e3d9ec8fe4953e42c7d59117c6eae70eae" + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/b22b59e3d9ec8fe4953e42c7d59117c6eae70eae", - "reference": "b22b59e3d9ec8fe4953e42c7d59117c6eae70eae", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" }, "require-dev": { "ext-filter": "*", "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevcs": "^1.2.0", "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" }, "type": "phpcodesniffer-standard", @@ -1591,15 +1640,15 @@ "type": "thanks_dev" } ], - "time": "2025-09-05T00:00:03+00:00" + "time": "2025-12-08T14:27:58+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.31", + "version": "2.2.5", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", - "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/909c1e5fef7989ac0d0c1c5c42e32a5c4f6198a0", + "reference": "909c1e5fef7989ac0d0c1c5c42e32a5c4f6198a0", "shasum": "" }, "require": { @@ -1622,6 +1671,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -1644,7 +1704,7 @@ "type": "github" } ], - "time": "2025-10-10T14:14:11+00:00" + "time": "2026-07-05T06:31:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3146,16 +3206,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.4", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119", - "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -3172,11 +3232,6 @@ "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -3226,20 +3281,20 @@ "type": "thanks_dev" } ], - "time": "2025-09-05T05:47:09+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "szepeviktor/phpstan-wordpress", - "version": "v2.0.2", + "version": "v2.0.3", "source": { "type": "git", "url": "https://github.com/szepeviktor/phpstan-wordpress.git", - "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8" + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/963887b04c21fe7ac78e61c1351f8b00fff9f8f8", - "reference": "963887b04c21fe7ac78e61c1351f8b00fff9f8f8", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/aa722f037b2d034828cd6c55ebe9e5c74961927e", + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e", "shasum": "" }, "require": { @@ -3249,6 +3304,7 @@ }, "require-dev": { "composer/composer": "^2.1.14", + "composer/semver": "^3.4", "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.1", "phpstan/phpstan-strict-rules": "^2.0", @@ -3286,9 +3342,9 @@ ], "support": { "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", - "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.2" + "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.3" }, - "time": "2025-02-12T18:43:37+00:00" + "time": "2025-09-14T02:58:22+00:00" }, { "name": "theseer/tokenizer", @@ -3342,16 +3398,16 @@ }, { "name": "wp-coding-standards/wpcs", - "version": "3.2.0", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "d2421de7cec3274ae622c22c744de9a62c7925af" + "reference": "469c18ceab4d642b15bad4c65ebf3b307bfd55ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af", - "reference": "d2421de7cec3274ae622c22c744de9a62c7925af", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/469c18ceab4d642b15bad4c65ebf3b307bfd55ab", + "reference": "469c18ceab4d642b15bad4c65ebf3b307bfd55ab", "shasum": "" }, "require": { @@ -3359,17 +3415,17 @@ "ext-libxml": "*", "ext-tokenizer": "*", "ext-xmlreader": "*", - "php": ">=5.4", - "phpcsstandards/phpcsextra": "^1.4.0", - "phpcsstandards/phpcsutils": "^1.1.0", - "squizlabs/php_codesniffer": "^3.13.0" + "php": ">=7.2", + "phpcsstandards/phpcsextra": "^1.5.0", + "phpcsstandards/phpcsutils": "^1.2.2", + "squizlabs/php_codesniffer": "^3.13.5" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/php-compatibility": "^10.0.0@dev", "phpcsstandards/phpcsdevtools": "^1.2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^8.0 || ^9.0" }, "suggest": { "ext-iconv": "For improved results", @@ -3404,7 +3460,7 @@ "type": "custom" } ], - "time": "2025-07-24T20:08:31+00:00" + "time": "2026-07-16T13:05:29+00:00" }, { "name": "yoast/phpunit-polyfills", diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..c3cbfc08 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,60 @@ +const defaultConfig = require( '@wordpress/scripts/config/eslint.config.cjs' ); + +module.exports = [ + ...defaultConfig, + { + ignores: [ 'assets/**', '**/*.d.ts' ], + }, + { + files: [ '**/*.{js,jsx,mjs,cjs}' ], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + Audio: 'readonly', + }, + }, + rules: { + 'import/default': 'off', + 'import/named': 'off', + 'import/no-duplicates': 'off', + 'import/no-extraneous-dependencies': 'off', + 'import/no-unresolved': 'off', + 'linebreak-style': [ 'error', 'unix' ], + 'array-bracket-spacing': [ + 'warn', + 'always', + { + arraysInArrays: false, + objectsInArrays: false, + }, + ], + 'key-spacing': [ + 'warn', + { + beforeColon: false, + afterColon: true, + }, + ], + 'object-curly-spacing': [ + 'warn', + 'always', + { + arraysInObjects: true, + objectsInObjects: false, + }, + ], + '@wordpress/i18n-text-domain': [ + 'error', + { + allowedTextDomain: 'hyve-lite', + }, + ], + }, + }, +]; diff --git a/hyve-lite.php b/hyve-lite.php index 62a34ea9..528d6e0d 100644 --- a/hyve-lite.php +++ b/hyve-lite.php @@ -60,9 +60,16 @@ function ( $products ) { } ); +add_filter( + HYVE_PRODUCT_SLUG . '_sdk_migrations_path', + function () { + return HYVE_LITE_PATH . '/migrations'; + } +); + add_action( 'plugins_loaded', function () { new \ThemeIsle\HyveLite\Main(); - } + } ); diff --git a/inc/API.php b/inc/API.php index 3e08fb66..3153616c 100644 --- a/inc/API.php +++ b/inc/API.php @@ -25,6 +25,16 @@ class API extends BaseAPI { */ private static $instance = null; + /** + * Source post IDs from the last knowledge base search, ordered by relevance + * (highest score first) and de-duplicated per source. + * + * Used to optionally append source links to the chat response. + * + * @var array + */ + private $source_post_ids = []; + /** * Ensures only one instance of the class is loaded. * @@ -82,9 +92,12 @@ function ( $result, $message_vector, $similarity_score_threshold, $max_tokens ) */ public function register_routes() { $namespace = $this->get_endpoint(); + if ( '' === $namespace || '0' === $namespace ) { + return; + } $routes = [ - 'settings' => [ + 'settings' => [ [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'get_settings' ], @@ -103,7 +116,7 @@ public function register_routes() { 'callback' => [ $this, 'update_settings' ], ], ], - 'data' => [ + 'data' => [ [ 'methods' => \WP_REST_Server::READABLE, 'args' => [ @@ -153,30 +166,48 @@ public function register_routes() { 'callback' => [ $this, 'delete_data' ], ], ], - 'threads' => [ + 'data/counts' => [ [ 'methods' => \WP_REST_Server::READABLE, - 'args' => [ + 'callback' => [ $this, 'get_data_counts' ], + ], + ], + 'stats' => [ + [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'get_stats' ], + ], + ], + 'threads' => [ + [ + 'methods' => \WP_REST_Server::READABLE, + 'args' => [ 'offset' => [ 'required' => false, 'type' => 'integer', 'default' => 0, ], ], - 'callback' => [ $this, 'get_threads' ], + 'callback' => [ $this, 'get_threads' ], + 'permission_callback' => function () { + return current_user_can( 'hyve_read_messages' ); + }, ], [ - 'methods' => \WP_REST_Server::DELETABLE, - 'args' => [ + 'methods' => \WP_REST_Server::DELETABLE, + 'args' => [ 'id' => [ 'required' => true, 'type' => 'integer', ], ], - 'callback' => [ $this, 'delete_thread' ], + 'callback' => [ $this, 'delete_thread' ], + 'permission_callback' => function () { + return current_user_can( 'hyve_manage_messages' ); + }, ], ], - 'qdrant' => [ + 'qdrant' => [ [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ $this, 'qdrant_status' ], @@ -186,7 +217,7 @@ public function register_routes() { 'callback' => [ $this, 'qdrant_deactivate' ], ], ], - 'chat' => [ + 'chat' => [ [ 'methods' => \WP_REST_Server::READABLE, 'args' => [ @@ -209,6 +240,10 @@ public function register_routes() { 'required' => false, 'type' => 'string', ], + 'is_test' => [ + 'required' => false, + 'type' => 'boolean', + ], ], 'callback' => [ $this, 'get_chat' ], 'permission_callback' => function ( $request ) { @@ -220,7 +255,7 @@ public function register_routes() { 'methods' => \WP_REST_Server::CREATABLE, 'args' => [ 'message' => [ - 'required' => true, + 'required' => false, 'type' => 'string', ], 'thread_id' => [ @@ -234,6 +269,15 @@ public function register_routes() { 'integer', ], ], + 'is_test' => [ + 'required' => false, + 'type' => 'boolean', + ], + 'mode' => [ + 'required' => false, + 'type' => 'string', + 'enum' => [ 'stream', 'background' ], + ], ], 'callback' => [ $this, 'send_chat' ], 'permission_callback' => function ( $request ) { @@ -289,7 +333,7 @@ public function update_settings( $request ) { } if ( empty( $updated ) ) { - return rest_ensure_response( [ 'error' => __( 'No settings to update.', 'hyve-lite' ) ] ); + return $this->settings_response( [ 'success' => __( 'Settings are already up to date.', 'hyve-lite' ) ] ); } $validation = apply_filters( @@ -313,11 +357,38 @@ public function update_settings( $request ) { }, 'sanitize' => 'sanitize_url', ], - 'chat_enabled' => [ + 'display_mode' => [ 'validate' => function ( $value ) { - return is_bool( $value ); + return in_array( $value, [ 'all', 'include', 'exclude', 'manual' ], true ); + }, + 'sanitize' => 'sanitize_text_field', + ], + 'display_rules' => [ + 'validate' => function ( $value ) { + return is_array( $value ); + }, + 'sanitize' => function ( $value ) { + if ( ! is_array( $value ) ) { + return []; + } + + $rules = []; + + foreach ( $value as $rule ) { + if ( ! is_array( $rule ) || empty( $rule['path'] ) ) { + continue; + } + + $operator = ( isset( $rule['operator'] ) && 'matches' === $rule['operator'] ) ? 'matches' : 'contains'; + + $rules[] = [ + 'path' => sanitize_text_field( $rule['path'] ), + 'operator' => $operator, + ]; + } + + return $rules; }, - 'sanitize' => 'rest_sanitize_boolean', ], 'welcome_message' => [ 'validate' => function ( $value ) { @@ -337,41 +408,45 @@ public function update_settings( $request ) { }, 'sanitize' => 'sanitize_text_field', ], - 'temperature' => [ + 'similarity_score_threshold' => [ 'validate' => function ( $value ) { return is_numeric( $value ); }, 'sanitize' => 'floatval', ], - 'top_p' => [ + 'post_row_addon_enabled' => [ 'validate' => function ( $value ) { - return is_numeric( $value ); + return is_bool( $value ); }, - 'sanitize' => 'floatval', + 'sanitize' => 'rest_sanitize_boolean', ], - 'moderation_threshold' => [ + 'sound_enabled' => [ 'validate' => function ( $value ) { - return is_array( $value ) && array_reduce( - $value, - function ( $carry, $item ) { - return $carry && is_int( $item ); - }, - true - ); + return is_bool( $value ); }, - 'sanitize' => function ( $value ) { - return array_map( 'intval', $value ); + 'sanitize' => 'rest_sanitize_boolean', + ], + 'show_timestamp' => [ + 'validate' => function ( $value ) { + return is_bool( $value ); }, + 'sanitize' => 'rest_sanitize_boolean', ], - 'similarity_score_threshold' => [ + 'privacy_notice_enabled' => [ 'validate' => function ( $value ) { - return is_numeric( $value ); + return is_bool( $value ); }, - 'sanitize' => 'floatval', + 'sanitize' => 'rest_sanitize_boolean', ], - 'post_row_addon_enabled' => [ + 'chat_position' => [ 'validate' => function ( $value ) { - return is_bool( $value ); + return in_array( $value, [ 'left', 'right' ], true ); + }, + 'sanitize' => 'sanitize_text_field', + ], + 'show_source_link' => [ + 'validate' => function ( $value ) { + return is_bool( $value ); }, 'sanitize' => 'rest_sanitize_boolean', ], @@ -387,8 +462,15 @@ function ( $carry, $item ) { ); foreach ( $updated as $key => $value ) { + // Unknown keys (e.g. settings removed in an update but still + // present in the stored option) are dropped, not fatal. + if ( ! isset( $validation[ $key ] ) ) { + unset( $updated[ $key ] ); + continue; + } + if ( ! $validation[ $key ]['validate']( $value ) ) { - return rest_ensure_response( + return $this->settings_response( [ // translators: %s: option key. 'error' => sprintf( __( 'Invalid value: %s', 'hyve-lite' ), $key ), @@ -399,15 +481,34 @@ function ( $carry, $item ) { $updated[ $key ] = $validation[ $key ]['sanitize']( $value ); } + $api_warning = ''; + $api_key_error = null; + $key_validated = false; + foreach ( $updated as $key => $value ) { $settings[ $key ] = $value; if ( 'api_key' === $key && ! empty( $value ) ) { - $openai = new OpenAI( $value ); - $valid_api = $openai->moderate( 'This is a test message.' ); + $openai = new OpenAI( $value ); + + // Validate against the embeddings endpoint. Suppress automatic + // persistence: the dashboard notice is reconciled after the save + // actually lands, so it can never reflect a key that was not + // stored. See Codeinwp/hyve#149. + $validation = $openai->set_error_persistence( false )->create_embeddings( 'Test connection.' ); + $key_validated = true; + + if ( is_wp_error( $validation ) && $this->is_auth_error( $validation->get_error_code() ) ) { + // The key itself is invalid — block the save. + return $this->settings_response( [ 'error' => $this->get_error_message( $validation ) ] ); + } - if ( is_wp_error( $valid_api ) ) { - return rest_ensure_response( [ 'error' => $this->get_error_message( $valid_api ) ] ); + if ( is_wp_error( $validation ) ) { + // The key is well-formed but the account is rate-limited or + // has no credits (new, unfunded accounts return a 429). Save + // the key but warn; the notice is recorded after the save. + $api_warning = $this->get_error_message( $validation ); + $api_key_error = $validation; } } @@ -421,13 +522,94 @@ function ( $carry, $item ) { $init = $qdrant->init(); if ( is_wp_error( $init ) ) { - return rest_ensure_response( [ 'error' => $this->get_error_message( $init ) ] ); + return $this->settings_response( [ 'error' => $this->get_error_message( $init ) ] ); + } + } + + if ( ! Encryption::has_key_changed() && ! Encryption::ensure_key_check() ) { + return $this->settings_response( [ 'error' => __( 'Unable to prepare encryption for connection credentials.', 'hyve-lite' ) ] ); + } + + if ( ! Main::save_settings( $settings ) ) { + return $this->settings_response( [ 'error' => __( 'Unable to encrypt connection credentials.', 'hyve-lite' ) ] ); + } + + Encryption::maybe_reset_key_check(); + + // Reconcile the dashboard service-error notice with the key that was just + // saved — only now that the save has actually landed (no earlier exit can + // leave a notice for an unsaved key). Clear any stale notice first, then + // re-record one for the new key when its error is actionable (a no-op for + // transient codes such as rate limits). See Codeinwp/hyve#149. + if ( $key_validated ) { + delete_option( OpenAI::ERROR_OPTION_KEY ); + + if ( null !== $api_key_error ) { + OpenAI::instance()->save_service_error( $api_key_error ); } } - update_option( 'hyve_settings', $settings ); + if ( ! empty( $api_warning ) ) { + return $this->settings_response( [ 'warning' => $api_warning ] ); + } + + return $this->settings_response( [ 'success' => __( 'Settings updated.', 'hyve-lite' ) ] ); + } + + /** + * Build a settings REST response carrying the current service errors. + * + * Returning the freshly-computed service errors lets the dashboard notice + * update immediately after a save, without a page reload. See Codeinwp/hyve#200. + * + * @param array $payload The response payload. + * + * @return \WP_REST_Response + */ + private function settings_response( $payload ) { + $options = apply_filters( 'hyve_options_data', [] ); + + $payload['serviceErrors'] = ( is_array( $options ) && isset( $options['serviceErrors'] ) ) ? $options['serviceErrors'] : []; + + return rest_ensure_response( $payload ); + } + + /** + * Whether an error code means the API key itself is invalid. + * + * These block a key from being saved. Account-level problems (no credits, + * billing, rate limits) do not: the key is valid, the account just needs + * attention, so we save it and warn instead of blocking. See Codeinwp/hyve#149. + * + * @param int|string $code The error code. + * + * @return bool + */ + private function is_auth_error( $code ) { + return in_array( $code, OpenAI::AUTH_ERROR_CODES, true ); + } + + /** + * Get the visibility of a post for the Knowledge Base UI. + * + * Content added to the Knowledge Base is surfaced to any chat visitor + * regardless of the post's original visibility, so the admin UI flags + * restricted content. + * + * @param int $post_id Post ID. + * + * @return string One of 'public', 'private' or 'password'. + */ + private function get_post_visibility( $post_id ) { + if ( 'private' === get_post_status( $post_id ) ) { + return 'private'; + } + + if ( '' !== get_post_field( 'post_password', $post_id ) ) { + return 'password'; + } - return rest_ensure_response( __( 'Settings updated.', 'hyve-lite' ) ); + return 'public'; } /** @@ -439,12 +621,11 @@ function ( $carry, $item ) { */ public function get_data( $request ) { $args = [ - 'post_type' => $request->get_param( 'type' ), - 'post_status' => 'publish', - 'posts_per_page' => 20, - 'fields' => 'ids', - 'offset' => $request->get_param( 'offset' ), - 'meta_query' => [ + 'post_type' => $request->get_param( 'type' ), + 'post_status' => [ 'publish', 'private' ], + 'fields' => 'ids', + 'offset' => $request->get_param( 'offset' ), + 'meta_query' => [ [ 'key' => '_hyve_added', 'compare' => 'NOT EXISTS', @@ -459,7 +640,8 @@ public function get_data( $request ) { $search = $request->get_param( 'search' ); if ( ! empty( $search ) ) { - $args['s'] = $search; + $args['s'] = $search; + $args['search_columns'] = [ 'post_title' ]; } $status = $request->get_param( 'status' ); @@ -504,45 +686,154 @@ public function get_data( $request ) { ]; } - $query = new \WP_Query( $args ); - - $posts_data = []; + /** + * Filters the WP_Query arguments of the dashboard data listings. + * + * Lets Pro include its own sources (custom data, links, documents) in + * the unified Knowledge Base listing. + * + * @param array $args WP_Query arguments. + * @param string $status Requested listing status. + * @param string $type Requested type filter. + */ + $args = apply_filters( 'hyve_data_query_args', $args, (string) $status, (string) $request->get_param( 'type' ) ); + + $page = $this->query_page( $args, 20, true ); + + $posts_data = []; + $chunk_counts = $this->table->get_counts_by_post_ids( $page['posts'] ); + + foreach ( $page['posts'] as $post_id ) { + /** + * The post id. + * + * @var int $post_id + */ + $post_type = get_post_type( $post_id ); + $post_type_object = $post_type ? get_post_type_object( $post_type ) : null; + + $post_data = [ + 'ID' => $post_id, + 'title' => html_entity_decode( get_the_title( $post_id ), ENT_QUOTES, 'UTF-8' ), + 'visibility' => $this->get_post_visibility( $post_id ), + 'type' => $post_type_object ? $post_type_object->labels->singular_name : $post_type, + 'chunks' => $chunk_counts[ $post_id ] ?? 0, + ]; - if ( $query->have_posts() ) { - foreach ( $query->posts as $post_id ) { - /** - * The post id. - * - * @var int $post_id - */ - $post_data = [ - 'ID' => $post_id, - 'title' => get_the_title( $post_id ), - ]; + if ( 'moderation' === $status ) { + $review = get_post_meta( $post_id, '_hyve_moderation_review', true ); - if ( 'moderation' === $status ) { - $review = get_post_meta( $post_id, '_hyve_moderation_review', true ); + if ( ! is_array( $review ) || empty( $review ) ) { + $review = []; + } - if ( ! is_array( $review ) || empty( $review ) ) { - $review = []; - } + $post_data['review'] = $review; + } - $post_data['review'] = $review; - } + $processing_error = get_post_meta( $post_id, '_hyve_processing_error', true ); - $posts_data[] = $post_data; + if ( ! empty( $processing_error ) && get_post_meta( $post_id, '_hyve_added', true ) ) { + $post_data['error'] = $processing_error; } + + /** + * Filters a row of the dashboard data listings. + * + * Pro decorates its own sources with their source label and the + * `permanent` deletion flag. + * + * @param array $post_data Row payload. + * @param int $post_id Post ID. + */ + $posts_data[] = apply_filters( 'hyve_data_post', $post_data, $post_id ); } $posts = [ 'posts' => $posts_data, - 'more' => $query->found_posts > 20, + 'more' => $page['more'], + 'total' => $page['total'], + 'per_page' => 20, 'totalChunks' => $this->table->get_count(), ]; return rest_ensure_response( $posts ); } + /** + * Count posts matching a meta query. + * + * @param array|string> $meta_query Meta query, including an optional `relation` key. + * + * @return int + */ + private function count_posts_by_meta( $meta_query ) { + $query = new \WP_Query( + [ + 'post_type' => 'any', + 'post_status' => [ 'publish', 'private' ], + 'fields' => 'ids', + 'posts_per_page' => 1, + 'no_found_rows' => false, + 'meta_query' => $meta_query, + ] + ); + + return intval( $query->found_posts ); + } + + /** + * Get the counts feeding the Needs Attention badge. + * + * @return \WP_REST_Response + */ + public function get_data_counts() { + $pending = $this->count_posts_by_meta( + [ + 'relation' => 'AND', + [ + 'key' => '_hyve_needs_update', + 'value' => '1', + 'compare' => '=', + ], + [ + 'key' => '_hyve_moderation_failed', + 'compare' => 'NOT EXISTS', + ], + ] + ); + + $moderation = $this->count_posts_by_meta( + [ + [ + 'key' => '_hyve_moderation_failed', + 'value' => '1', + 'compare' => '=', + ], + ] + ); + + return rest_ensure_response( + [ + 'pending' => $pending, + 'moderation' => $moderation, + ] + ); + } + + /** + * Get the dashboard stats and chart data. + * + * @return \WP_REST_Response + */ + public function get_stats() { + return rest_ensure_response( + [ + 'stats' => apply_filters( 'hyve_stats', [] ), + 'chart' => apply_filters( 'hyve_chart_data', [] ), + ] + ); + } + /** * Add data. * @@ -574,6 +865,20 @@ public function add_data( $request ) { return rest_ensure_response( [ 'error' => $this->get_error_message( $process ) ] ); } + // The content was stored, but the synchronous indexing attempt may have + // failed (e.g. rate limit, no credits). Surface that as a non-blocking + // warning so the admin sees it immediately, not only as a list badge. + $processing_error = get_post_meta( $post_id, '_hyve_processing_error', true ); + + if ( ! empty( $processing_error ) ) { + return rest_ensure_response( + [ + 'success' => true, + 'warning' => $processing_error, + ] + ); + } + return rest_ensure_response( true ); } @@ -592,8 +897,8 @@ public function delete_data( $request ) { try { $delete_result = Qdrant_API::instance()->delete_point( $id ); - if ( ! $delete_result ) { - throw new \Exception( __( 'Failed to delete point in Qdrant.', 'hyve-lite' ) ); + if ( is_wp_error( $delete_result ) || ! $delete_result ) { + throw new \Exception( is_wp_error( $delete_result ) ? $delete_result->get_error_message() : __( 'Failed to delete point in Qdrant.', 'hyve-lite' ) ); } } catch ( \Exception $e ) { return rest_ensure_response( [ 'error' => $e->getMessage() ] ); @@ -606,6 +911,18 @@ public function delete_data( $request ) { delete_post_meta( $id, '_hyve_needs_update' ); delete_post_meta( $id, '_hyve_moderation_failed' ); delete_post_meta( $id, '_hyve_moderation_review' ); + delete_post_meta( $id, '_hyve_processing_error' ); + + /** + * Fires after content is removed from the Knowledge Base. + * + * Pro uses it to delete its own entries (custom data, links, + * documents), which have no life outside the Knowledge Base. + * + * @param int $id Post ID. + */ + do_action( 'hyve_data_deleted', (int) $id ); + return rest_ensure_response( true ); } @@ -648,40 +965,39 @@ public function get_threads( $request ) { $pages = apply_filters( 'hyve_threads_per_page', 3 ); $args = [ - 'post_type' => 'hyve_threads', - 'post_status' => 'publish', - 'posts_per_page' => $pages, - 'fields' => 'ids', - 'offset' => $request->get_param( 'offset' ), + 'post_type' => 'hyve_threads', + 'post_status' => 'publish', + 'fields' => 'ids', + 'offset' => $request->get_param( 'offset' ), ]; - $query = new \WP_Query( $args ); + $page = $this->query_page( $args, $pages ); $posts_data = []; - if ( $query->have_posts() ) { - foreach ( $query->posts as $post_id ) { - /** - * The post id. - * - * @var int $post_id - */ - - $post_data = [ - 'ID' => $post_id, - 'title' => get_the_title( $post_id ), - 'date' => get_the_date( 'c', $post_id ), - 'thread' => get_post_meta( $post_id, '_hyve_thread_data', true ), - 'thread_id' => get_post_meta( $post_id, '_hyve_thread_id', true ), - ]; + foreach ( $page['posts'] as $post_id ) { + /** + * The post id. + * + * @var int $post_id + */ + + $post_data = [ + 'ID' => $post_id, + 'title' => html_entity_decode( get_the_title( $post_id ), ENT_QUOTES, 'UTF-8' ), + 'date' => get_the_date( 'c', $post_id ), + 'thread' => get_post_meta( $post_id, '_hyve_thread_data', true ), + 'thread_id' => get_post_meta( $post_id, '_hyve_thread_id', true ), + ]; - $posts_data[] = $post_data; - } + $posts_data[] = $post_data; } $posts = [ - 'posts' => $posts_data, - 'more' => $query->found_posts > $pages, + 'posts' => $posts_data, + 'more' => $page['more'], + 'total' => intval( Threads::get_thread_count() ), + 'per_page' => $pages, ]; return rest_ensure_response( $posts ); @@ -731,7 +1047,7 @@ public function qdrant_deactivate() { $settings['qdrant_api_key'] = ''; $settings['qdrant_endpoint'] = ''; - update_option( 'hyve_settings', $settings ); + Main::save_settings( $settings ); update_option( 'hyve_qdrant_status', 'inactive' ); delete_option( 'hyve_qdrant_migration' ); @@ -781,31 +1097,45 @@ function ( $message ) { return rest_ensure_response( [ 'error' => __( 'No messages found.', 'hyve-lite' ) ] ); } - $message = reset( $message )->content[0]->text; - $message = json_decode( $message, true ); + $text = reset( $message )->content[0]->text; - if ( json_last_error() !== JSON_ERROR_NONE ) { - return rest_ensure_response( [ 'error' => __( 'No messages found.', 'hyve-lite' ) ] ); - } - Main::add_labels_to_default_settings(); $settings = Main::get_settings(); - if ( isset( $message['properties'] ) ) { - $message = $message['properties']; + $interpreted = OpenAI::interpret_chat_payload( $text, $settings['default_message'] ); + + if ( ! $interpreted['decoded'] ) { + return rest_ensure_response( [ 'error' => __( 'No messages found.', 'hyve-lite' ) ] ); } - $response = ( isset( $message['success'] ) && true === $message['success'] && isset( $message['response'] ) ) ? $message['response'] : esc_html( $settings['default_message'] ); + $payload = $interpreted['payload']; + $response = $interpreted['final']; + $answered = $interpreted['answered']; - do_action( 'hyve_chat_response', $run_id, $thread_id, $query, $record_id, $message, $response ); + if ( ! empty( $settings['show_source_link'] ) && $answered ) { + $response = $this->append_source_link( $response, $run_id ); + } + // Skip recording for admin live-preview test chats (see send_chat). + if ( ! $request->get_param( 'is_test' ) ) { + do_action( 'hyve_chat_response', $run_id, $thread_id, $query, $record_id, $payload, $response ); + } - return rest_ensure_response( - [ - 'status' => $status, - 'success' => isset( $message['success'] ) ? $message['success'] : false, - 'message' => $response, - ] - ); + $data = [ + 'status' => $status, + 'success' => $answered, + 'message' => $response, + ]; + + // Let extensions attach extra reply data (e.g. follow-up suggestions from + // the structured payload). Shared with the streaming flow (Stream) so both + // paths surface the same data to the widget. + $reply = apply_filters( 'hyve_chat_reply_data', $data, $payload, $answered ); + + if ( is_array( $reply ) ) { + $data = $reply; + } + + return rest_ensure_response( $data ); } /** @@ -827,6 +1157,8 @@ private function search_knowledge_base_qdrant( $message_vector, $similarity_scor return $articles_embedded_data; } + $source_scores = []; + foreach ( $knowledge_points as $point ) { if ( empty( $point['post_title'] ) || empty( $point['post_content'] ) || empty( $point['token_count'] ) ) { continue; @@ -838,10 +1170,20 @@ private function search_knowledge_base_qdrant( $message_vector, $similarity_scor continue; } + if ( isset( $point['post_id'], $point['score'] ) ) { + $post_id = (string) $point['post_id']; + + if ( ! isset( $source_scores[ $post_id ] ) || $point['score'] > $source_scores[ $post_id ] ) { + $source_scores[ $post_id ] = $point['score']; + } + } + $articles_embedded_data .= "\n ===START POST=== " . $point['post_title'] . ' - ' . $point['post_content'] . ' ===END POST==='; $current_token_count += intval( $point['token_count'] ); } + $this->source_post_ids = $this->rank_sources( $source_scores ); + return $articles_embedded_data; } @@ -933,18 +1275,75 @@ function ( $a, $b ) { return $articles_embedded_data; } + $source_scores = []; + foreach ( $matched_articles as $article ) { $article_data = $this->table->get_post_data( $article['id'] ); if ( empty( $article_data ) ) { continue; } + $post_id = $this->table->get_post_id( $article['id'] ); + + if ( ! empty( $post_id ) && ( ! isset( $source_scores[ $post_id ] ) || $article['score'] > $source_scores[ $post_id ] ) ) { + $source_scores[ $post_id ] = $article['score']; + } + $articles_embedded_data .= "\n ===START POST=== " . $article_data['post_title'] . ' - ' . $article_data['post_content'] . ' ===END POST==='; } + $this->source_post_ids = $this->rank_sources( $source_scores ); + return $articles_embedded_data; } + /** + * Order de-duplicated sources by relevance, drop weak matches and return + * their post IDs. + * + * A single fixed score cutoff does not travel well — the score of a genuinely + * relevant match varies a lot between queries — so sources are kept relative + * to the best match: anything scoring within a ratio of the top result is + * kept, and the low-scoring stragglers that merely cleared the context + * threshold are dropped. The strongest source is always retained. + * + * @since 1.4.2 + * + * @param array $source_scores Map of source post ID to its best score. + * + * @return array Post IDs ordered by score, highest first. + */ + private function rank_sources( $source_scores ) { + if ( empty( $source_scores ) ) { + return []; + } + + arsort( $source_scores ); + + $top_score = reset( $source_scores ); + + /** + * Filters how close to the best match a source must score to be shown as + * a source link, as a ratio of the top score (0–1). A higher value keeps + * only near-equal matches; 0 keeps everything above the context threshold. + * + * @since 1.4.2 + * + * @param float $ratio The minimum score ratio relative to the best match. Default 0.8. + */ + $ratio = (float) apply_filters( 'hyve_source_link_score_ratio', 0.8 ); + $threshold = $top_score * $ratio; + + $source_scores = array_filter( + $source_scores, + function ( $score ) use ( $threshold ) { + return $score >= $threshold; + } + ); + + return array_keys( $source_scores ); + } + /** * Get Similarity. * @@ -955,6 +1354,8 @@ function ( $a, $b ) { * @return string The articles blob data that match the given message vector. */ public function search_knowledge_base( $message_vector, $similarity_score_threshold = 0.4, $tokens_threshold = 2000 ) { + $this->source_post_ids = []; + if ( Qdrant_API::is_active() ) { return $this->search_knowledge_base_qdrant( $message_vector, $similarity_score_threshold, $tokens_threshold ); } @@ -962,6 +1363,261 @@ public function search_knowledge_base( $message_vector, $similarity_score_thresh return $this->search_knowledge_base_wp( $message_vector, $similarity_score_threshold, $tokens_threshold ); } + /** + * Build the text used for knowledge base retrieval. + * + * Retrieval embeds this text and searches the knowledge base with it. For the + * first message it is just the question. For follow-ups it also blends in the + * most recent turns of the conversation, so a topic-less question such as + * "How difficult is it?" still carries the subject ("pickleball") into the + * search and matches the relevant content, instead of embedding a query with + * no topic that finds nothing. The model already receives the conversation + * history through the OpenAI conversation; this closes the same gap for + * retrieval. + * + * @param string $message The current user message. + * @param int|string $record_id The thread post ID, when the conversation exists. + * @param string $thread_id The OpenAI conversation ID, when one exists. + * + * @return string + */ + private function build_retrieval_query( $message, $record_id, $thread_id = '' ) { + $history = []; + + if ( ! empty( $record_id ) && 'hyve_threads' === get_post_type( (int) $record_id ) ) { + $thread_data = get_post_meta( (int) $record_id, '_hyve_thread_data', true ); + + if ( is_array( $thread_data ) ) { + $history = $thread_data; + } + } + + /** + * Filters how many recent messages are blended into the retrieval query. + * + * Set to 0 to disable conversation-aware retrieval and search with the + * current message only. + * + * @since 1.5.0 + * + * @param int $count Number of most recent messages to include. Default 6. + * @param string $thread_id The OpenAI conversation ID, when one exists. + */ + $count = (int) apply_filters( 'hyve_retrieval_history_count', 6, $thread_id ); + + /** + * Filters the per-message character cap for the retrieval query. + * + * Keeps a single long turn from dominating or bloating the embedded query. + * + * @since 1.5.0 + * + * @param int $length Maximum characters kept per message. Default 500. + */ + $length = (int) apply_filters( 'hyve_retrieval_history_message_length', 500 ); + + $parts = []; + + if ( $count > 0 && ! empty( $history ) ) { + $recent = array_slice( $history, - $count ); + + foreach ( $recent as $entry ) { + if ( empty( $entry['message'] ) ) { + continue; + } + + $text = trim( wp_strip_all_tags( (string) $entry['message'] ) ); + + if ( '' === $text ) { + continue; + } + + if ( mb_strlen( $text ) > $length ) { + $text = mb_substr( $text, 0, $length ); + } + + $parts[] = $text; + } + } + + // The current question goes last so it carries the most weight. + $parts[] = $message; + + $query = implode( "\n", $parts ); + + /** + * Filters the final text used for knowledge base retrieval. + * + * Allows replacing the assembled query, for example with a rewritten + * standalone question, before it is embedded and searched. + * + * @since 1.5.0 + * + * @param string $query The assembled retrieval query. + * @param string $message The current user message. + * @param array> $history The thread history considered. + */ + return apply_filters( 'hyve_retrieval_query', $query, $message, $history ); + } + + /** + * Resolve a public source link for a knowledge base source post. + * + * Returns a link only for publicly accessible content. Regular WordPress + * posts are linked when their visibility is public; other source types + * (e.g. the pro plugin's website links or custom data) are handled through + * the `hyve_chat_source_link` filter. + * + * @since 1.4.2 + * + * @param int|string $post_id Source post ID. + * + * @return string Public URL, or empty string when no link should be shown. + */ + private function resolve_source_link( $post_id ) { + $default = ''; + $source_id = (int) $post_id; + $post_type = get_post_type( $source_id ); + + if ( + $post_type && + 'public' === $this->get_post_visibility( $source_id ) && + is_post_type_viewable( $post_type ) + ) { + $permalink = get_permalink( $source_id ); + $default = $permalink ? $permalink : ''; + } + + /** + * Filters the public source link appended to a chat answer. + * + * Return an empty string to omit the link (for content that is not + * publicly accessible, such as custom data). The pro plugin uses this + * to resolve website-URL sources and to suppress links for custom data. + * + * @since 1.4.2 + * + * @param string $default The default resolved URL (empty for non-public content). + * @param int|string $post_id The source post ID. + */ + $url = apply_filters( 'hyve_chat_source_link', $default, $post_id ); + + return ! empty( $url ) ? esc_url_raw( $url ) : ''; + } + + /** + * Append a "Answer provided based on" source link to a chat response. + * + * @since 1.4.2 + * + * @param string $response The chat response HTML. + * @param string $run_id The run ID used to look up the stored source. + * + * @return string The response, with the source link appended when available. + */ + private function append_source_link( $response, $run_id ) { + $transient_key = 'hyve_source_' . $run_id; + $post_ids = get_transient( $transient_key ); + delete_transient( $transient_key ); + + return $this->maybe_append_source_link( $response, $post_ids ); + } + + /** + * Append source links to a chat response for the publicly accessible sources. + * + * Renders up to a filterable number of links (default 3), keeping the highest + * scoring public sources and skipping any that have no public URL. + * + * @since 1.4.2 + * + * @param string $response The chat response HTML. + * @param array|int|string $post_ids Source post IDs, ordered by relevance. A single ID is accepted for convenience. + * + * @return string + */ + public function maybe_append_source_link( $response, $post_ids ) { + if ( empty( $post_ids ) ) { + return $response; + } + + $post_ids = is_array( $post_ids ) ? $post_ids : [ $post_ids ]; + + /** + * Filters the maximum number of source links appended to a chat answer. + * + * @since 1.4.2 + * + * @param int $limit The maximum number of source links. Default 3. + */ + $limit = (int) apply_filters( 'hyve_source_link_limit', 3 ); + + if ( $limit < 1 ) { + return $response; + } + + $links = []; + + foreach ( $post_ids as $post_id ) { + $link = $this->build_source_link( $post_id, count( $links ) + 1 ); + + if ( '' === $link ) { + continue; + } + + $links[] = $link; + + if ( count( $links ) >= $limit ) { + break; + } + } + + if ( empty( $links ) ) { + return $response; + } + + $intro = '' . esc_html__( 'Sources', 'hyve-lite' ) . ''; + + return $response . '
' . $intro . implode( '', $links ) . '
'; + } + + /** + * Build the markup for a single numbered source link, or an empty string + * when the source is not publicly accessible. + * + * @since 1.4.2 + * + * @param int|string $post_id The source post ID. + * @param int $number The 1-based position shown on the citation chip. + * + * @return string + */ + private function build_source_link( $post_id, $number ) { + if ( empty( $post_id ) ) { + return ''; + } + + $url = $this->resolve_source_link( $post_id ); + + if ( empty( $url ) ) { + return ''; + } + + $title = get_the_title( (int) $post_id ); + $title = ! empty( $title ) ? $title : $url; + + /* translators: 1: citation number, 2: source page title. */ + $label = sprintf( __( 'Source %1$d: %2$s', 'hyve-lite' ), $number, $title ); + + return sprintf( + '%3$d%4$s', + esc_url( $url ), + esc_attr( $label ), + (int) $number, + esc_html( $title ) + ); + } + /** * Send chat. * @@ -970,23 +1626,111 @@ public function search_knowledge_base( $message_vector, $similarity_score_thresh * @return \WP_REST_Response */ public function send_chat( $request ) { - $message = $request->get_param( 'message' ); - $record_id = $request->get_param( 'record_id' ); + $prepared = $this->prepare_chat( $request ); + + if ( is_wp_error( $prepared ) ) { + return rest_ensure_response( + [ + 'error' => $this->get_error_message( $prepared ), + 'code' => $prepared->get_error_code(), + ] + ); + } + + $is_test = (bool) $request->get_param( 'is_test' ); + $request_record = $request->get_param( 'record_id' ); + $request_record = $request_record ? $request_record : null; + + // Streaming path: stash the prepared turn and hand back a token the + // streaming endpoint (admin-ajax) will use to generate and record the + // reply. The user message is recorded there, once, when the reply lands. + if ( 'stream' === $request->get_param( 'mode' ) ) { + $token = wp_generate_password( 24, false ); + + set_transient( + 'hyve_stream_job_' . $token, + [ + 'thread_id' => $prepared['thread_id'], + 'record_id' => $request_record, + 'message' => $prepared['message'], + 'context' => $prepared['context'], + 'is_test' => $is_test, + 'source_post_ids' => $this->source_post_ids, + ], + 5 * MINUTE_IN_SECONDS + ); + + return rest_ensure_response( + [ + 'thread_id' => $prepared['thread_id'], + 'record_id' => $request_record, + 'stream_token' => $token, + 'content' => $prepared['context'], + ] + ); + } + + // Default path: background run + client polling (unchanged behavior). + $thread_id = $prepared['thread_id']; + $query_run = $this->create_background_run( $prepared['context'], $prepared['message'], $thread_id ); + + if ( is_wp_error( $query_run ) ) { + return rest_ensure_response( [ 'error' => $this->get_error_message( $query_run ) ] ); + } + + // Test chats from the admin live preview are not recorded as threads, so + // they never pollute the conversation history or the analytics charts. + $record_id = $is_test ? null : apply_filters( 'hyve_chat_request', $thread_id, $request_record, $prepared['message'] ); + + return rest_ensure_response( + [ + 'thread_id' => $thread_id, + 'query_run' => $query_run, + 'record_id' => $record_id ? $record_id : null, + 'content' => $prepared['context'], + ] + ); + } + + /** + * Prepare a chat turn before a model run is created. + * + * Runs the shared, transport-agnostic work: moderation, embeddings, + * knowledge base search and conversation/thread creation. Used by both the + * streaming and the background reply flows so each runs once per turn. The + * user message is recorded by the caller (so it happens exactly once, + * whichever flow ultimately answers). + * + * @param \WP_REST_Request> $request Request. + * + * @return array{thread_id:string,message:string,context:string}|\WP_Error + */ + private function prepare_chat( $request ) { + $message = $request->get_param( 'message' ); + + if ( empty( $message ) ) { + return new \WP_Error( 'missing_message', __( 'Message was flagged.', 'hyve-lite' ) ); + } + $moderation = OpenAI::instance()->moderate_chunks( $message ); if ( true !== $moderation ) { - return rest_ensure_response( [ 'error' => __( 'Message was flagged.', 'hyve-lite' ) ] ); + return new \WP_Error( 'content_flagged', __( 'Message was flagged.', 'hyve-lite' ) ); } - $openai = OpenAI::instance(); - $message_vector = $openai->create_embeddings( $message ); - $message_vector = reset( $message_vector ); - $message_vector = $message_vector->embedding; + $openai = OpenAI::instance(); + $record_id = $request->get_param( 'record_id' ); + $record_id = $record_id ? $record_id : null; + $retrieval_query = $this->build_retrieval_query( $message, $record_id, $request->get_param( 'thread_id' ) ); + $message_vector = $openai->create_embeddings( $retrieval_query ); if ( is_wp_error( $message_vector ) ) { - return rest_ensure_response( [ 'error' => __( 'No embeddings found.', 'hyve-lite' ) ] ); + return new \WP_Error( 'no_embeddings', __( 'No embeddings found.', 'hyve-lite' ) ); } + $message_vector = reset( $message_vector ); + $message_vector = $message_vector->embedding; + if ( $request->get_param( 'thread_id' ) ) { $thread_id = $request->get_param( 'thread_id' ); } else { @@ -994,7 +1738,7 @@ public function send_chat( $request ) { } if ( is_wp_error( $thread_id ) ) { - return rest_ensure_response( [ 'error' => $this->get_error_message( $thread_id ) ] ); + return $thread_id; } /** @@ -1013,64 +1757,52 @@ public function send_chat( $request ) { $article_context = $this->search_knowledge_base( $message_vector, $similarity_score_threshold ); - $query_run = $openai->create_response( - [ - [ - 'type' => 'message', - 'role' => 'user', - 'content' => 'START CONTEXT: ' . $article_context . ' :END CONTEXT', - ], - [ - 'type' => 'message', - 'role' => 'user', - 'content' => 'START QUESTION: ' . $message . ' :END QUESTION', - ], - ], - $thread_id - ); + $hash = hash( 'md5', strtolower( $message ) ); + // TTL must outlast the slowest reply (streaming can run up to the 120s + // cURL cap) so the embedding is still available when hyve_chat_response + // fires and unanswered-question analytics can read it. + set_transient( 'hyve_message_' . $hash, $message_vector, 5 * MINUTE_IN_SECONDS ); + + return [ + 'thread_id' => $thread_id, + 'message' => $message, + 'context' => $article_context, + ]; + } - if ( is_wp_error( $query_run ) ) { - if ( strpos( $this->get_error_message( $query_run ), 'Conversation with id' ) !== false ) { - $thread_id = $openai->create_conversation(); + /** + * Create a background model run for the poll-based reply flow. + * + * Mirrors the original send_chat behavior, including the retry when the + * conversation has expired on OpenAI's side. + * + * @param string $context Knowledge base context. + * @param string $message User message. + * @param string $thread_id Conversation id (by reference; recreated if expired). + * + * @return string|\WP_Error Run id or error. + */ + private function create_background_run( $context, $message, &$thread_id ) { + $openai = OpenAI::instance(); - if ( is_wp_error( $thread_id ) ) { - return rest_ensure_response( [ 'error' => $this->get_error_message( $thread_id ) ] ); - } + $items = OpenAI::build_chat_items( $context, $message ); - $query_run = $openai->create_response( - [ - [ - 'type' => 'message', - 'role' => 'user', - 'content' => 'START CONTEXT: ' . $article_context . ' :END CONTEXT', - ], - [ - 'type' => 'message', - 'role' => 'user', - 'content' => 'START QUESTION: ' . $message . ' :END QUESTION', - ], - ], - $thread_id - ); + $query_run = $openai->create_response( $items, $thread_id ); - if ( is_wp_error( $query_run ) ) { - return rest_ensure_response( [ 'error' => $this->get_error_message( $query_run ) ] ); - } - } - } + if ( is_wp_error( $query_run ) && strpos( $this->get_error_message( $query_run ), 'Conversation with id' ) !== false ) { + $new_thread = $openai->create_conversation(); - $hash = hash( 'md5', strtolower( $message ) ); - set_transient( 'hyve_message_' . $hash, $message_vector, MINUTE_IN_SECONDS ); + if ( is_wp_error( $new_thread ) ) { + return $new_thread; + } - $record_id = apply_filters( 'hyve_chat_request', $thread_id, $record_id, $message ); + $thread_id = $new_thread; + $query_run = $openai->create_response( $items, $thread_id ); + } - return rest_ensure_response( - [ - 'thread_id' => $thread_id, - 'query_run' => $query_run, - 'record_id' => $record_id ? $record_id : null, - 'content' => $article_context, - ] - ); + if ( ! empty( $this->source_post_ids ) && is_string( $query_run ) ) { + set_transient( 'hyve_source_' . $query_run, $this->source_post_ids, HOUR_IN_SECONDS ); + } + return $query_run; } } diff --git a/inc/BaseAPI.php b/inc/BaseAPI.php index 9265752c..f4626f7e 100644 --- a/inc/BaseAPI.php +++ b/inc/BaseAPI.php @@ -51,12 +51,10 @@ public function __construct() { * @return string */ public function get_error_message( $error ) { - $errors = [ - 'invalid_api_key' => __( 'Incorrect API key provided.', 'hyve-lite' ), - 'missing_scope' => __( 'You have insufficient permissions for this operation.', 'hyve-lite' ), - ]; - if ( isset( $errors[ $error->get_error_code() ] ) ) { - return $errors[ $error->get_error_code() ]; + $message = OpenAI::get_error_message_for_code( $error->get_error_code() ); + + if ( null !== $message ) { + return $message; } return $error->get_error_message(); @@ -64,10 +62,53 @@ public function get_error_message( $error ) { /** * Get endpoint. - * + * * @return string */ public function get_endpoint() { return $this->namespace . '/' . $this->version; } + + /** + * Run a paginated query and detect whether more rows exist. + * + * Fetches one row beyond the page size to tell whether there is a next page, + * which avoids the found-rows count (SQL_CALC_FOUND_ROWS) that + * WP_Query::$found_posts otherwise requires. The extra row is trimmed off the + * returned set. Pass `$with_total` when the caller drives a numbered pager; + * that trades the trick for the found-rows count and adds `total`. + * + * @param array $args WP_Query arguments, without paging. + * @param int $per_page Page size. + * @param bool $with_total Also count all matching rows. + * + * @return ($with_total is true ? array{posts: array, more: bool, total: int} : array{posts: array, more: bool}) + */ + protected function query_page( $args, $per_page = 20, $with_total = false ) { + if ( $with_total ) { + $args['posts_per_page'] = $per_page; + $args['no_found_rows'] = false; + + $query = new \WP_Query( $args ); + $total = intval( $query->found_posts ); + + return [ + 'posts' => $query->posts, + 'more' => intval( $args['offset'] ?? 0 ) + count( $query->posts ) < $total, + 'total' => $total, + ]; + } + + $args['posts_per_page'] = $per_page + 1; + $args['no_found_rows'] = true; + + $query = new \WP_Query( $args ); + $posts = $query->posts; + $more = count( $posts ) > $per_page; + + return [ + 'posts' => array_slice( $posts, 0, $per_page ), + 'more' => $more, + ]; + } } diff --git a/inc/DB_Table.php b/inc/DB_Table.php index 4357a38a..ad87523f 100644 --- a/inc/DB_Table.php +++ b/inc/DB_Table.php @@ -40,6 +40,14 @@ class DB_Table { */ const CACHE_PREFIX = 'hyve-'; + /** + * Maximum number of times a chunk is retried after a transient failure + * before it is marked failed. See Codeinwp/hyve#199. + * + * @var int + */ + const MAX_PROCESS_ATTEMPTS = 5; + /** * The single instance of the class. * @@ -69,7 +77,15 @@ public function __construct() { global $wpdb; $this->table_name = $wpdb->prefix . 'hyve'; - add_action( 'hyve_process_post', [ $this, 'process_post' ], 10, 1 ); + add_action( + 'hyve_process_post', + function ( $id ) { + // Discard the return value: a cron/action callback must not return anything. + $this->process_post( $id ); + }, + 10, + 1 + ); add_action( 'hyve_delete_posts', [ $this, 'delete_posts' ], 10, 1 ); add_action( 'hyve_update_posts', [ $this, 'update_posts' ] ); @@ -92,7 +108,6 @@ public function __construct() { public function create_table() { global $wpdb; - // @phpstan-ignore requireOnce.fileNotFound require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $sql = 'CREATE TABLE ' . $this->table_name . ' ( @@ -226,6 +241,7 @@ public function insert( array $data ): int { $this->delete_cache( 'entries' ); $this->delete_cache( 'entries_count' ); + $this->delete_cache( 'chunk_counts' ); $this->delete_cache( 'cached_embeddings' ); return $wpdb->insert_id; @@ -275,6 +291,7 @@ public function delete_by_post_id( $post_id ) { $this->delete_cache( 'entries' ); $this->delete_cache( 'entries_processed' ); $this->delete_cache( 'entries_count' ); + $this->delete_cache( 'chunk_counts' ); $this->delete_cache( 'cached_embeddings' ); return $rows_affected; @@ -402,7 +419,7 @@ public function get_post_data( $id ) { global $wpdb; $post = $wpdb->get_row( $wpdb->prepare( 'SELECT post_title, post_content FROM %i WHERE id = %d', $this->table_name, $id ), ARRAY_A ); - + if ( empty( $post ) ) { return null; } @@ -412,6 +429,21 @@ public function get_post_data( $id ) { return $post; } + /** + * Get the source post ID for a chunk row. + * + * @since 1.4.2 + * + * @param int $id Row ID in the table. + * + * @return string|null Source post ID, or null if the row does not exist. + */ + public function get_post_id( $id ) { + global $wpdb; + + return $wpdb->get_var( $wpdb->prepare( 'SELECT post_id FROM %i WHERE id = %d', $this->table_name, $id ) ); + } + /** * Update storage of all rows. * @@ -466,27 +498,96 @@ public function get_posts_over_limit() { * @throws \Exception If Qdrant API fails. */ public function add_post( $post_id, $action = 'add' ) { - $data = [ - 'ID' => $post_id, - 'title' => get_the_title( $post_id ), - 'content' => apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) ), - ]; - update_post_meta( $post_id, '_hyve_post_processing', 1 ); - $data = Tokenizer::tokenize( $data ); - $chunks = array_column( $data, 'post_content' ); - $moderation = OpenAI::instance()->moderate_chunks( $chunks, $post_id ); + $result = $this->ingest_document( + [ + 'title' => get_the_title( $post_id ), + 'content' => apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) ), + ], + [ + 'action' => 'update' === $action ? 'update' : 'add', + 'override' => 'override' === $action, + 'post_id' => $post_id, + 'create' => false, + 'persist_moderation' => true, + 'retry_async' => 'update' === $action, + ] + ); + + delete_post_meta( $post_id, '_hyve_post_processing' ); + + return $result; + } + + /** + * Ingest a document into the knowledge base. + * + * Shared pipeline for every data source (posts, custom data, links and + * sitemap entries): tokenize -> moderate -> (replace old chunks on update) + * -> insert chunk rows -> embed. The only things that vary between sources + * are how the owning post is resolved and what extra meta it carries, both + * expressed through `$args`. + * + * @since 1.4.0 + * + * @param array $doc The document, with `title` and `content` keys. + * @param array $args { + * Optional. Ingestion options. + * + * @type string $action 'add' or 'update'. Default 'add'. + * @type bool $override Skip the moderation gate. Default false. + * @type int|null $post_id Existing post to attach chunks to. Required + * for the posts source and for any update. + * @type bool $create Create (add) or update a managed `hyve_docs` + * post. False for the posts source, where the + * WP post already exists and is left untouched. + * Default false. + * @type string $post_type Post type to create when `create` is true. + * Default 'hyve_docs'. + * @type array $meta Extra post meta to set on success. + * @type bool $persist_moderation Store/clear `_hyve_moderation_*` meta on + * `post_id`. Default false. + * @type bool $retry_async Retry a failed embedding in the background + * via cron. When false, failures are terminal + * and recorded for immediate, synchronous + * surfacing to the caller. Default true. + * } + * + * @return true|\WP_Error + * @throws \Exception If Qdrant API fails. + */ + public function ingest_document( $doc, $args = [] ) { + $action = $args['action'] ?? 'add'; + $override = ! empty( $args['override'] ); + $create = ! empty( $args['create'] ); + $post_id = $args['post_id'] ?? null; + $post_type = $args['post_type'] ?? 'hyve_docs'; + $extra_meta = $args['meta'] ?? []; + $persist_moderation = ! empty( $args['persist_moderation'] ); + $allow_retry = $args['retry_async'] ?? true; + + $data = Tokenizer::tokenize( + [ + 'ID' => $post_id, + 'title' => $doc['title'], + 'content' => $doc['content'], + ] + ); + $chunks = array_column( $data, 'post_content' ); + // Only reuse the per-post moderation cache for an existing WP post + // (the posts source). Freshly submitted content is always re-moderated. + $moderation = OpenAI::instance()->moderate_chunks( $chunks, $persist_moderation ? $post_id : null ); if ( is_wp_error( $moderation ) ) { - delete_post_meta( $post_id, '_hyve_post_processing' ); return $moderation; } - if ( true !== $moderation && 'override' !== $action ) { - delete_post_meta( $post_id, '_hyve_post_processing' ); - update_post_meta( $post_id, '_hyve_moderation_failed', 1 ); - update_post_meta( $post_id, '_hyve_moderation_review', $moderation ); + if ( true !== $moderation && ! $override ) { + if ( $persist_moderation && $post_id ) { + update_post_meta( $post_id, '_hyve_moderation_failed', 1 ); + update_post_meta( $post_id, '_hyve_moderation_review', $moderation ); + } return new \WP_Error( 'content_failed_moderation', @@ -495,34 +596,83 @@ public function add_post( $post_id, $action = 'add' ) { ); } + // Resolve the owning post. Moderation has passed, so creating/updating a + // managed post here cannot leave an orphan behind on rejection. if ( 'update' === $action ) { if ( Qdrant_API::is_active() ) { try { $delete_result = Qdrant_API::instance()->delete_point( $post_id ); - if ( ! $delete_result ) { - throw new \Exception( __( 'Failed to delete point in Qdrant.', 'hyve-lite' ) ); + if ( is_wp_error( $delete_result ) || ! $delete_result ) { + throw new \Exception( is_wp_error( $delete_result ) ? $delete_result->get_error_message() : __( 'Failed to delete point in Qdrant.', 'hyve-lite' ) ); } } catch ( \Exception $e ) { - delete_post_meta( $post_id, '_hyve_post_processing' ); return new \WP_Error( 'qdrant_error', $e->getMessage() ); } } $this->delete_by_post_id( $post_id ); + + if ( $create ) { + $updated = wp_update_post( + [ + 'ID' => $post_id, + 'post_title' => $doc['title'], + 'post_content' => $doc['content'], + ] + ); + + if ( ! $updated ) { + return new \WP_Error( 'failed_update_post', __( 'Failed to update post.', 'hyve-lite' ) ); + } + } + } elseif ( $create ) { + $post_id = wp_insert_post( + [ + 'post_title' => $doc['title'], + 'post_content' => $doc['content'], + 'post_status' => 'publish', + 'post_type' => $post_type, + ] + ); + + if ( ! $post_id ) { + return new \WP_Error( 'failed_insert_post', __( 'Failed to insert post.', 'hyve-lite' ) ); + } } + $processing_error = null; + foreach ( $data as $datum ) { - $id = $this->insert( $datum ); - $this->process_post( $id ); + $datum['post_id'] = $post_id; + + $id = $this->insert( $datum ); + $result = $this->process_post( $id, $allow_retry ); + + if ( is_wp_error( $result ) && null === $processing_error ) { + $processing_error = $result; + } } - delete_post_meta( $post_id, '_hyve_post_processing' ); update_post_meta( $post_id, '_hyve_added', 1 ); - delete_post_meta( $post_id, '_hyve_moderation_failed' ); - delete_post_meta( $post_id, '_hyve_moderation_review' ); - delete_post_meta( $post_id, '_hyve_needs_update' ); - $this->delete_cache( 'cached_embeddings' ); + + foreach ( $extra_meta as $meta_key => $meta_value ) { + update_post_meta( $post_id, $meta_key, $meta_value ); + } + + // When retries are disabled a chunk failure is terminal, so record the + // reason against the post. This also restores the error if a later + // chunk's success cleared it mid-loop, keeping the surfaced reason honest. + if ( ! $allow_retry && null !== $processing_error ) { + $this->record_processing_error( $post_id, $processing_error, false ); + } + + if ( $persist_moderation ) { + delete_post_meta( $post_id, '_hyve_moderation_failed' ); + delete_post_meta( $post_id, '_hyve_moderation_review' ); + delete_post_meta( $post_id, '_hyve_needs_update' ); + $this->delete_cache( 'cached_embeddings' ); + } return true; } @@ -532,20 +682,34 @@ public function add_post( $post_id, $action = 'add' ) { * * @since 1.2.0 * - * @param int $id The post ID. - * - * @return void + * @param int $id The post ID. + * @param bool $allow_retry Whether a transient failure may be retried + * asynchronously via the hyve_process_post cron. + * When false, a failure is terminal and returned to + * the caller for immediate, synchronous handling. + * Default true. + * + * @return true|\WP_Error True on success, or the error encountered. */ - public function process_post( $id ) { - $post = $this->get( $id ); - $content = $post->post_content; - $openai = OpenAI::instance(); - $stripped = wp_strip_all_tags( $content ); + public function process_post( $id, $allow_retry = true ) { + $post = $this->get( $id ); + $content = $post->post_content; + $openai = OpenAI::instance(); + $stripped = wp_strip_all_tags( $content ); + + // Prepend the title to the text sent for embedding so it contributes to the vector. + if ( ! empty( $post->post_title ) ) { + $stripped = $post->post_title . ' ' . $stripped; + } $embeddings = $openai->create_embeddings( $stripped ); if ( is_wp_error( $embeddings ) || ! $embeddings ) { - wp_schedule_single_event( time() + 60, 'hyve_process_post', [ $id ] ); - return; + $error = is_wp_error( $embeddings ) + ? $embeddings + : new \WP_Error( 'unknown_error', __( 'An unexpected error occurred while indexing this content.', 'hyve-lite' ) ); + + $this->handle_processing_failure( $id, (int) $post->post_id, $error, $allow_retry ); + return $error; } $embeddings = reset( $embeddings ); @@ -571,8 +735,8 @@ public function process_post( $id ) { } if ( is_wp_error( $success ) ) { - wp_schedule_single_event( time() + 60, 'hyve_process_post', [ $id ] ); - return; + $this->handle_processing_failure( $id, (int) $post->post_id, $success, $allow_retry ); + return $success; } } @@ -584,8 +748,84 @@ public function process_post( $id ) { 'embeddings' => $embeddings, 'post_status' => 'processed', 'storage' => $storage, - ] + ] ); + + // Processing succeeded; clear any error and retry counter from a previous attempt. + delete_post_meta( (int) $post->post_id, '_hyve_processing_error' ); + delete_transient( self::CACHE_PREFIX . 'process_attempts_' . $id ); + + return true; + } + + /** + * Handle a failed processing attempt. + * + * Fatal errors (bad key, no credits, billing) will not resolve by retrying, + * so they stop immediately and the entry is marked failed. Transient errors + * (rate limits, network blips) are retried with a linear backoff up to + * MAX_PROCESS_ATTEMPTS, then also marked failed. Either way the reason is + * recorded for the Knowledge Base UI. See Codeinwp/hyve#199. + * + * @since 1.4.0 + * + * @param int $id The chunk row ID. + * @param int $post_id The source post ID. + * @param \WP_Error $error The error encountered while processing. + * @param bool $allow_retry Whether an async retry may be scheduled. When + * false the failure is terminal. Default true. + * + * @return void + */ + private function handle_processing_failure( $id, $post_id, $error, $allow_retry = true ) { + $transient = self::CACHE_PREFIX . 'process_attempts_' . $id; + $attempts = (int) get_transient( $transient ) + 1; + $is_fatal = OpenAI::is_fatal_error_code( $error->get_error_code() ); + $will_retry = $allow_retry && ! $is_fatal && $attempts < self::MAX_PROCESS_ATTEMPTS; + + $this->record_processing_error( $post_id, $error, $will_retry ); + + if ( $will_retry ) { + set_transient( $transient, $attempts, DAY_IN_SECONDS ); + wp_schedule_single_event( time() + ( MINUTE_IN_SECONDS * $attempts ), 'hyve_process_post', [ $id ] ); + return; + } + + // Terminal: stop retrying and mark the chunk failed. + delete_transient( $transient ); + $this->update( $id, [ 'post_status' => 'failed' ] ); + } + + /** + * Record a processing error against the source post so it can be surfaced + * in the Knowledge Base UI instead of failing silently. The stored message + * includes whether the entry will be retried or needs the admin to act. + * See Codeinwp/hyve#199. + * + * @since 1.4.0 + * + * @param int $post_id The source post ID. + * @param \WP_Error $error The error encountered while processing. + * @param bool $will_retry Whether another attempt is scheduled. + * + * @return void + */ + private function record_processing_error( $post_id, $error, $will_retry ) { + if ( empty( $post_id ) ) { + return; + } + + $message = OpenAI::get_error_message_for_code( $error->get_error_code() ); + + if ( null === $message ) { + $message = $error->get_error_message(); + } + + $suffix = $will_retry + ? __( 'It will be retried automatically.', 'hyve-lite' ) + : __( 'Please fix the problem and re-add this content.', 'hyve-lite' ); + + update_post_meta( $post_id, '_hyve_processing_error', $message . ' ' . $suffix ); } /** @@ -598,7 +838,7 @@ public function process_post( $id ) { public function update_posts() { $args = [ 'post_type' => 'any', - 'post_status' => 'publish', + 'post_status' => [ 'publish', 'private' ], 'posts_per_page' => 5, 'fields' => 'ids', 'meta_query' => [ @@ -690,6 +930,44 @@ public function get_count() { return $count; } + /** + * Get chunk counts for a set of posts. + * + * The full per-post map is cached and invalidated together with + * `entries_count`, so listings do not re-run the aggregate per page. + * + * @since 1.4.0 + * + * @param array $post_ids The post IDs. + * + * @return array Chunk counts keyed by post ID. + */ + public function get_counts_by_post_ids( $post_ids ) { + $post_ids = array_filter( array_map( 'intval', $post_ids ) ); + + if ( empty( $post_ids ) ) { + return []; + } + + $counts = $this->get_cache( 'chunk_counts' ); + + if ( ! is_array( $counts ) ) { + global $wpdb; + + $results = $wpdb->get_results( $wpdb->prepare( 'SELECT post_id, COUNT(*) AS chunks FROM %i GROUP BY post_id', $this->table_name ) ); + + $counts = []; + + foreach ( $results as $row ) { + $counts[ intval( $row->post_id ) ] = intval( $row->chunks ); + } + + $this->set_cache( 'chunk_counts', $counts ); + } + + return array_intersect_key( $counts, array_flip( $post_ids ) ); + } + /** * Return cache. * diff --git a/inc/Encryption.php b/inc/Encryption.php new file mode 100644 index 00000000..b8d671d4 --- /dev/null +++ b/inc/Encryption.php @@ -0,0 +1,172 @@ + $iv_size + self::AUTH_TAG_LENGTH; + } + + /** + * Create the encrypted key check marker if this is a new installation. + * + * @return bool Whether the active key is usable. + */ + public static function ensure_key_check() { + $check = get_option( self::KEY_CHECK_OPTION, '' ); + + if ( '' === $check ) { + $encrypted = self::encrypt( self::KEY_CHECK_VALUE ); + + return false !== $encrypted && update_option( self::KEY_CHECK_OPTION, $encrypted ); + } + + return self::KEY_CHECK_VALUE === self::decrypt( $check ); + } + + /** + * Check whether the configured encryption key has changed. + * + * @return bool + */ + public static function has_key_changed() { + $check = get_option( self::KEY_CHECK_OPTION, '' ); + + return '' !== $check && self::KEY_CHECK_VALUE !== self::decrypt( $check ); + } + + /** + * Clear a changed-key marker once every plugin has recovered its secrets. + * + * @return bool Whether the marker is valid or was successfully recovered. + */ + public static function maybe_reset_key_check() { + if ( ! self::has_key_changed() ) { + return self::ensure_key_check(); + } + + $can_reset = apply_filters( 'hyve_encryption_key_check_can_reset', true ); + + if ( ! $can_reset ) { + return false; + } + + delete_option( self::KEY_CHECK_OPTION ); + + return self::ensure_key_check(); + } + + /** + * Derive a fixed-length encryption key. + * + * @return string + */ + private static function key() { + $key = defined( 'SECURE_AUTH_KEY' ) ? constant( 'SECURE_AUTH_KEY' ) : wp_salt( 'secure_auth' ); + + if ( ! is_string( $key ) ) { + $key = wp_salt( 'secure_auth' ); + } + + return hash( 'sha256', $key, true ); + } +} diff --git a/inc/Main.php b/inc/Main.php index 9b8dba75..82b9df34 100644 --- a/inc/Main.php +++ b/inc/Main.php @@ -12,6 +12,7 @@ use ThemeIsle\HyveLite\Threads; use ThemeIsle\HyveLite\API; use ThemeIsle\HyveLite\Qdrant_API; +use ThemeIsle\HyveLite\Stream; /** * Class Main @@ -52,14 +53,17 @@ public function __construct() { new Block(); new Threads(); + new Stream(); add_action( 'admin_menu', [ $this, 'register_menu_page' ] ); + add_filter( 'user_has_cap', [ $this, 'grant_message_capabilities' ] ); add_action( 'save_post', [ $this, 'update_meta' ], 10, 3 ); add_action( 'delete_post', [ $this, 'delete_post' ] ); add_filter( 'themeisle_sdk_enable_telemetry', '__return_true' ); add_filter( 'hyve_global_chat_enabled', [ $this, 'is_global_chat_enabled' ] ); add_filter( 'hyve_stats', [ $this, 'get_stats' ] ); + add_filter( 'hyve_chart_data', [ $this, 'get_chart_data' ] ); add_filter( 'hyve_options_data', [ $this, 'append_services_error' ] ); add_filter( 'hyve_similarity_score_threshold', [ $this, 'get_similarity_threshold_score' ] ); @@ -97,7 +101,86 @@ function ( $data, $page_slug ) { } add_filter( 'themeisle_sdk_blackfriday_data', [ $this, 'add_black_friday_data' ] ); + add_filter( 'hyve_lite_about_us_metadata', [ $this, 'about_us_metadata' ] ); add_action( 'admin_init', [ $this, 'admin_init' ] ); + add_action( 'admin_init', [ $this, 'add_privacy_policy_content' ] ); + add_action( 'admin_notices', [ $this, 'encryption_key_notice' ] ); + add_filter( 'hyve_encryption_key_check_can_reset', [ __CLASS__, 'can_reset_encryption_key_check' ] ); + } + + /** + * Warn administrators when encrypted credentials cannot be decrypted. + * + * @return void + */ + public function encryption_key_notice() { + if ( ! current_user_can( 'manage_options' ) || ! Encryption::has_key_changed() ) { + return; + } + ?> +
+

+
+ ' . + __( 'This information is provided to help you disclose how the Hyve chat assistant processes visitor data. Review it and adapt it to your site before publishing.', 'hyve-lite' ) . + '

' . + '

' . __( 'When visitors use the Hyve chat assistant on this site, the messages they send are stored on this website so the site administrator can review chat history. No account is required to use the chat.', 'hyve-lite' ) . '

' . + '

' . __( 'To generate replies, the messages are also sent to OpenAI, L.L.C. — a third-party service based in the United States. OpenAI processes the messages to moderate their content, to create numerical representations (embeddings) used to find relevant information, and to generate the assistant\'s responses.', 'hyve-lite' ) . '

' . + '

' . __( 'For details on how OpenAI handles data, see OpenAI\'s privacy policy at https://openai.com/policies/privacy-policy/.', 'hyve-lite' ) . '

'; + + // Only disclose Qdrant when it is actually connected, so the suggested text reflects the site's real data flows. + if ( Qdrant_API::is_active() ) { + $content .= '

' . __( 'This site also uses Qdrant, a third-party vector database. A numerical representation (embedding) of your message is sent to Qdrant to look up relevant information. See Qdrant\'s privacy policy at https://qdrant.tech/legal/privacy-policy/.', 'hyve-lite' ) . '

'; + } + + wp_add_privacy_policy_content( 'Hyve', wp_kses_post( $content ) ); + } + + /** + * Grant the Messages capabilities to administrators. + * + * The two capabilities are custom, so nobody has them by default. Granting + * them to anyone who can `manage_options` keeps the Messages submenu and its + * REST endpoints working for admins. Doing it here, instead of persisting to + * the role, means there is nothing to clean up on uninstall. + * + * To give access to other roles, add the capabilities to them with a + * role-editor plugin or WP_Role::add_cap(): + * - `hyve_read_messages` view the Messages page and read conversations. + * - `hyve_manage_messages` delete conversations and export them. + * + * @since 1.5.0 + * + * @param array $allcaps All capabilities of the current user. + * + * @return array + */ + public function grant_message_capabilities( $allcaps ) { + if ( ! empty( $allcaps['manage_options'] ) ) { + $allcaps['hyve_read_messages'] = true; + $allcaps['hyve_manage_messages'] = true; + } + + return $allcaps; } /** @@ -108,17 +191,102 @@ function ( $data, $page_slug ) { * @return void */ public function register_menu_page() { - $page_hook_suffix = add_menu_page( + $hook = add_menu_page( __( 'Hyve', 'hyve-lite' ), __( 'Hyve', 'hyve-lite' ), - 'manage_options', + 'hyve_read_messages', 'hyve', [ $this, 'menu_page' ], 'dashicons-format-chat', 99 ); - add_action( "admin_print_scripts-$page_hook_suffix", [ $this, 'enqueue_options_assets' ] ); + if ( $hook ) { + add_action( "admin_print_scripts-$hook", [ $this, 'enqueue_options_assets' ] ); + } + + global $submenu; + + foreach ( $this->get_submenu_pages() as $submenu_page ) { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- WordPress requires submenu entries to be registered through this global. + $submenu['hyve'][] = [ + $submenu_page['label'], + $submenu_page['capability'], + add_query_arg( + [ + 'page' => 'hyve', + 'nav' => $submenu_page['route'], + ], + admin_url( 'admin.php' ) + ), + $submenu_page['label'], + ]; + } + + /* + * Keep the dashboard tab active when WordPress renders a canonical + * `page=hyve&nav=...` submenu URL. + */ + add_filter( + 'submenu_file', + function ( $submenu_file, $parent_file ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reading a sanitized admin URL parameter to identify the active menu item; no state change. + $current_page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; + if ( 'hyve' !== $parent_file || 'hyve' !== $current_page ) { + return $submenu_file; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading a sanitized admin URL parameter to identify the active menu item; no state change. + $nav = isset( $_GET['nav'] ) ? sanitize_key( wp_unslash( $_GET['nav'] ) ) : 'dashboard'; + + return add_query_arg( + [ + 'page' => 'hyve', + 'nav' => $nav, + ], + admin_url( 'admin.php' ) + ); + }, + 10, + 2 + ); + } + + /** + * Get the Hyve submenu pages. + * + * Each entry mirrors a top-level section of the dashboard app and deep-links + * into it. The `route` is passed to the app so the matching screen opens. + * Messages is gated on `hyve_read_messages` so support staff can reach it + * without seeing the rest of the dashboard. + * + * @since 1.5.0 + * + * @return array + */ + public function get_submenu_pages() { + return [ + 'hyve' => [ + 'label' => __( 'Dashboard', 'hyve-lite' ), + 'capability' => 'manage_options', + 'route' => 'dashboard', + ], + 'hyve-knowledge-base' => [ + 'label' => __( 'Knowledge Base', 'hyve-lite' ), + 'capability' => 'manage_options', + 'route' => 'kb', + ], + 'hyve-messages' => [ + 'label' => __( 'Messages', 'hyve-lite' ), + 'capability' => 'hyve_read_messages', + 'route' => 'messages', + ], + 'hyve-settings' => [ + 'label' => __( 'Settings', 'hyve-lite' ), + 'capability' => 'manage_options', + 'route' => 'settings', + ], + ]; } /** @@ -152,27 +320,64 @@ public function admin_init() { ]; } + $submenu_pages = $this->get_submenu_pages(); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading the current admin page to set the document title, no state change. + $current_nav = isset( $_GET['nav'] ) ? sanitize_key( wp_unslash( $_GET['nav'] ) ) : 'dashboard'; + global $title; + foreach ( $submenu_pages as $submenu_page ) { + if ( $submenu_page['route'] === $current_nav ) { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- WordPress reads the current admin page title from this global. + $title = $submenu_page['label']; + break; + } + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading the current admin page to pick the initial app screen, no state change. + $current_page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : 'hyve'; + $current_view = 'hyve' === $current_page ? $current_nav : ( $submenu_pages[ $current_page ]['route'] ?? 'home' ); + add_filter( 'hyve_options_data', - function ( $data ) use ( $settings, $post_types_for_js ) { + /** + * Localize the dashboard data. + * + * @param array $data Localized dashboard data. + * + * @return array + */ + function ( $data ) use ( $settings, $post_types_for_js, $current_view ) { + /** + * PHPStan false positive: the return type is an array, but PHPStan cannot infer it because of the dynamic nature of the filter. + * + * @phpstan-ignore return.type + */ return array_merge( $data, [ - 'api' => $this->api->get_endpoint(), - 'rest_url' => rest_url( $this->api->get_endpoint() ), - 'postTypes' => $post_types_for_js, - 'hasAPIKey' => isset( $settings['api_key'] ) && ! empty( $settings['api_key'] ), - 'chunksLimit' => apply_filters( 'hyve_chunks_limit', 500 ), - 'isQdrantActive' => Qdrant_API::is_active(), - 'assets' => [ + 'view' => $current_view, + 'canManage' => current_user_can( 'manage_options' ), + 'canReadMessages' => current_user_can( 'hyve_read_messages' ), + 'canManageMessages' => current_user_can( 'hyve_manage_messages' ), + 'api' => $this->api->get_endpoint(), + 'version' => HYVE_LITE_VERSION, + 'rest_url' => rest_url( $this->api->get_endpoint() ), + 'postTypes' => $post_types_for_js, + 'hasAPIKey' => isset( $settings['api_key'] ) && ! empty( $settings['api_key'] ), + 'isApiKeyConnected' => self::is_api_key_connected( $settings ), + 'chunksLimit' => apply_filters( 'hyve_chunks_limit', 500 ), + 'isQdrantActive' => Qdrant_API::is_active(), + 'assets' => [ 'images' => HYVE_LITE_URL . 'assets/images/', ], - 'stats' => $this->get_stats(), - 'docs' => 'https://docs.themeisle.com/article/2009-hyve-documentation', - 'qdrant_docs' => 'https://docs.themeisle.com/article/2066-integrate-hyve-with-qdrant', - 'pro' => 'https://themeisle.com/plugins/hyve/', - 'chart' => $this->get_chart_data(), - 'hasPro' => apply_filters( 'product_hyve_license_status', false ), + 'stats' => $this->get_stats(), + 'privacySettings' => admin_url( 'options-privacy.php' ), + 'hasPrivacyPage' => '' !== get_privacy_policy_url(), + 'docs' => 'https://docs.themeisle.com/article/2009-hyve-documentation', + 'qdrant_docs' => 'https://docs.themeisle.com/article/2066-integrate-hyve-with-qdrant', + 'pro' => 'https://themeisle.com/plugins/hyve/', + 'chart' => $this->get_chart_data(), + 'hasPro' => apply_filters( 'product_hyve_license_status', false ), ] ); }, @@ -189,6 +394,13 @@ function ( $data ) use ( $settings, $post_types_for_js ) { */ public function enqueue_options_assets() { + /** + * Fires before the Hyve dashboard assets are enqueued, + * + * @since 1.5.0 + */ + do_action( 'hyve_enqueue_options_assets' ); + // @phpstan-ignore include.fileNotFound $asset_file = include HYVE_LITE_PATH . '/build/backend/index.asset.php'; @@ -215,6 +427,8 @@ public function enqueue_options_assets() { apply_filters( 'hyve_options_data', [] ) ); + $this->enqueue_chat_preview(); + do_action( 'themeisle_internal_page', HYVE_PRODUCT_SLUG, 'dashboard' ); } @@ -232,27 +446,18 @@ public static function get_default_settings() { 'api_key' => '', 'qdrant_api_key' => '', 'qdrant_endpoint' => '', - 'chat_enabled' => true, - 'chat_model' => 'gpt-4o-mini', - 'temperature' => 1, - 'top_p' => 1, - 'moderation_threshold' => [ - 'sexual' => 80, - 'hate' => 70, - 'harassment' => 70, - 'self-harm' => 50, - 'sexual/minors' => 50, - 'hate/threatening' => 60, - 'violence/graphic' => 80, - 'self-harm/intent' => 50, - 'self-harm/instructions' => 50, - 'harassment/threatening' => 60, - 'violence' => 70, - ], + 'chat_model' => 'gpt-5.4-nano', 'welcome_message' => '', 'default_message' => '', 'similarity_score_threshold' => 0.4, 'post_row_addon_enabled' => true, + 'sound_enabled' => true, + 'show_timestamp' => true, + 'privacy_notice_enabled' => false, + 'chat_position' => 'right', + 'show_source_link' => false, + 'display_mode' => 'all', + 'display_rules' => [], ] ); } @@ -265,11 +470,100 @@ public static function get_default_settings() { * @return array */ public static function get_settings() { - $settings = get_option( 'hyve_settings', [] ); + $saved = get_option( 'hyve_settings', [] ); + + if ( ! is_array( $saved ) ) { + $saved = []; + } + + foreach ( self::get_encrypted_settings() as $key ) { + if ( ! isset( $saved[ $key ] ) ) { + continue; + } + + $decrypted = Encryption::decrypt( $saved[ $key ] ); + $saved[ $key ] = false === $decrypted ? '' : $decrypted; + } + $settings = $saved; $settings['telemetry_enabled'] = 'yes' === get_option( 'hyve_lite_logger_flag', 'no' ); - return wp_parse_args( $settings, self::get_default_settings() ); + $settings = wp_parse_args( $settings, self::get_default_settings() ); + + /* + * Backward compatibility: derive the visibility mode from the legacy + * chat_enabled boolean until the migration persists display_mode. Only + * fires on the front-end window before the SDK migration runs (admin_init + * after an upgrade), so it can be removed once all installs migrated. + */ + if ( ! isset( $saved['display_mode'] ) ) { + $settings['display_mode'] = ( isset( $saved['chat_enabled'] ) && ! $saved['chat_enabled'] ) ? 'manual' : 'all'; + } + + return $settings; + } + + /** + * Get settings that must be encrypted at rest. + * + * @return string[] + */ + public static function get_encrypted_settings() { + return [ 'api_key', 'qdrant_api_key' ]; + } + + /** + * Persist settings while keeping sensitive values encrypted at rest. + * + * @param mixed $settings Settings to persist. + * @return bool Whether the settings were saved successfully. + */ + public static function save_settings( $settings ) { + if ( ! is_array( $settings ) ) { + return false; + } + + foreach ( self::get_encrypted_settings() as $key ) { + if ( ! isset( $settings[ $key ] ) || '' === $settings[ $key ] ) { + continue; + } + + $encrypted = Encryption::encrypt( $settings[ $key ] ); + + if ( false === $encrypted ) { + return false; + } + + $settings[ $key ] = $encrypted; + } + + return update_option( 'hyve_settings', $settings ) || get_option( 'hyve_settings' ) === $settings; + } + + /** + * Keep the changed-key marker until Lite's unreadable credentials are replaced. + * + * @param bool $can_reset Whether other plugin components are recovered. + * @return bool Whether Lite's credentials are recovered too. + */ + public static function can_reset_encryption_key_check( $can_reset ) { + if ( ! $can_reset ) { + return false; + } + + $settings = get_option( 'hyve_settings', [] ); + + if ( ! is_array( $settings ) ) { + return true; + } + + foreach ( self::get_encrypted_settings() as $key ) { + if ( isset( $settings[ $key ] ) && Encryption::is_encrypted( $settings[ $key ] ) && false === Encryption::decrypt( $settings[ $key ] ) ) { + return false; + } + } + + return true; } /** @@ -297,6 +591,53 @@ function ( $settings ) { ); } + /** + * The built-in chat icon slugs that can be inlined. + * + * @return string[] + */ + public static function get_known_icon_slugs() { + return [ + 'chat-bubble-left-ellipsis', + 'chat-bubble-oval-left', + 'chat-bubble-bottom-center-text', + 'chat-bubble-bottom-center', + 'chat-bubble-left', + 'chat-bubble-left-right', + ]; + } + + /** + * Read and inline the given built-in chat icon SVGs. + * + * Only known bundled icons are read (allowlist), and only the plugin's own + * asset files — never remote data. + * + * @param string[] $slugs Icon slugs to inline. + * + * @return array Map of slug => SVG markup. + */ + public static function get_inline_icons( $slugs ) { + $known = self::get_known_icon_slugs(); + $icons = []; + + foreach ( array_unique( $slugs ) as $slug ) { + if ( ! in_array( $slug, $known, true ) ) { + continue; + } + + $icon_path = HYVE_LITE_PATH . '/assets/icons/' . $slug . '.svg'; + + if ( is_readable( $icon_path ) ) { + // Reading a bundled plugin asset, not remote data. + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown + $icons[ $slug ] = trim( (string) file_get_contents( $icon_path ) ); + } + } + + return $icons; + } + /** * Enqueue assets. * @@ -329,6 +670,42 @@ public function enqueue_assets() { wp_set_script_translations( 'hyve-lite-scripts', 'hyve-lite' ); + wp_localize_script( 'hyve-lite-scripts', 'hyveClient', $this->get_frontend_data() ); + + $settings = self::get_settings(); + $should_display_chat = $this->should_display_chat(); + + if ( ! $should_display_chat ) { + return; + } + + wp_enqueue_style( 'hyve-styles' ); + wp_enqueue_script( 'hyve-lite-scripts' ); + + $has_pro = apply_filters( 'product_hyve_license_status', false ); + + if ( $has_pro ) { + return; + } + + wp_add_inline_script( + 'hyve-lite-scripts', + 'document.addEventListener("DOMContentLoaded", function() { const box = document.querySelector( ".hyve-input-box" ); if ( ! box ) { return; } const c = document.createElement("div"); c.className = "hyve-credits"; c.innerHTML = "Powered by Hyve"; if ( document.querySelector( ".hyve-privacy-notice" ) ) { c.hidden = true; } box.before( c ); });' + ); + } + + /** + * Build the data localized for the chat widget. + * + * Shared by the public frontend and the dashboard test preview so the two + * stay in sync. The `hyve_frontend_data` filter lets the Pro plugin layer + * appearance (name, icon, colors) on top. + * + * @param array $overrides Values merged over the defaults (e.g. preview flags). + * + * @return array + */ + public function get_frontend_data( $overrides = [] ) { self::add_labels_to_default_settings(); $settings = self::get_settings(); $stats = $this->get_stats(); @@ -342,54 +719,138 @@ public function enqueue_assets() { */ $should_show_chat = apply_filters( 'hyve_display_chat', 0 < intval( $stats['totalChunks'] ) ); - wp_localize_script( - 'hyve-lite-scripts', - 'hyveClient', - apply_filters( - 'hyve_frontend_data', - [ - 'api' => $this->api->get_endpoint(), - 'audio' => [ - 'click' => HYVE_LITE_URL . 'assets/audio/click.mp3', - 'ping' => HYVE_LITE_URL . 'assets/audio/ping.mp3', - ], - 'welcome' => esc_html( $settings['welcome_message'] ?? '' ), - 'isEnabled' => $settings['chat_enabled'], - 'strings' => [ - 'reply' => __( 'Write a reply…', 'hyve-lite' ), - 'suggestions' => __( 'Not sure where to start?', 'hyve-lite' ), - 'tryAgain' => __( 'Sorry, I am not able to process your request at the moment. Please try again.', 'hyve-lite' ), - 'typing' => __( 'Typing…', 'hyve-lite' ), - 'clearConversation' => __( 'Clear Conversation', 'hyve-lite' ), - ], - 'icons' => [ - 'chat-bubble-oval-left' => esc_url( HYVE_LITE_URL . 'assets/icons/chat-bubble-oval-left.svg' ), - 'chat-bubble-bottom-center-text' => esc_url( HYVE_LITE_URL . 'assets/icons/chat-bubble-bottom-center-text.svg' ), - 'chat-bubble-bottom-center' => esc_url( HYVE_LITE_URL . 'assets/icons/chat-bubble-bottom-center.svg' ), - 'chat-bubble-left' => esc_url( HYVE_LITE_URL . 'assets/icons/chat-bubble-left.svg' ), - 'chat-bubble-left-right' => esc_url( HYVE_LITE_URL . 'assets/icons/chat-bubble-left-right.svg' ), - ], - 'canShow' => $should_show_chat, - ] - ) - ); + // Inline the icon SVGs so the chat button renders instantly, without an + // extra runtime fetch (which causes an icon flash on load). Only the + // icons the chat can actually use are read: the default, plus the + // selected built-in icon — not every bundled file on each request. + $selected_icon = ( isset( $settings['chat_icon']['type'], $settings['chat_icon']['value'] ) && 'svg' === $settings['chat_icon']['type'] ) + ? (string) $settings['chat_icon']['value'] + : ''; - if ( ! isset( $settings['chat_enabled'] ) || false === $settings['chat_enabled'] ) { - return; + $icon_slugs = [ 'chat-bubble-left-ellipsis' ]; + + if ( in_array( $selected_icon, self::get_known_icon_slugs(), true ) ) { + $icon_slugs[] = $selected_icon; } - wp_enqueue_style( 'hyve-styles' ); - wp_enqueue_script( 'hyve-lite-scripts' ); + $data = apply_filters( + 'hyve_frontend_data', + [ + 'api' => $this->api->get_endpoint(), + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'streamNonce' => wp_create_nonce( Stream::NONCE_ACTION ), + 'audio' => [ + 'ping' => HYVE_LITE_URL . 'assets/audio/ping.mp3', + ], + 'welcome' => esc_html( $settings['welcome_message'] ?? '' ), + 'isEnabled' => $this->should_display_chat(), + 'soundEnabled' => boolval( $settings['sound_enabled'] ?? true ), + 'showTimestamp' => boolval( $settings['show_timestamp'] ?? true ), + 'chatPosition' => 'left' === ( $settings['chat_position'] ?? 'right' ) ? 'left' : 'right', + 'privacyNotice' => [ + 'enabled' => boolval( $settings['privacy_notice_enabled'] ?? false ), + /** + * Filters the URL the chat privacy notice links to. + * + * Defaults to the site's Privacy Policy page (Settings → Privacy). + * Return an empty string to render the notice without a link. + * + * @since 1.5.0 + * + * @param string $url The privacy policy URL. + */ + 'url' => (string) apply_filters( 'hyve_privacy_notice_url', get_privacy_policy_url() ), + ], + 'strings' => [ + 'title' => __( 'AI Assistant', 'hyve-lite' ), + 'status' => __( 'Online', 'hyve-lite' ), + 'reply' => __( 'Write a reply…', 'hyve-lite' ), + 'suggestions' => __( 'Not sure where to start?', 'hyve-lite' ), + 'tryAgain' => __( 'Sorry, I am not able to process your request at the moment. Please try again.', 'hyve-lite' ), + 'typing' => __( 'Typing…', 'hyve-lite' ), + 'clearConversation' => __( 'Clear Conversation', 'hyve-lite' ), + 'muteSound' => __( 'Mute Sound', 'hyve-lite' ), + 'unmuteSound' => __( 'Unmute Sound', 'hyve-lite' ), + 'openChat' => __( 'Open chat', 'hyve-lite' ), + 'closeChat' => __( 'Close chat', 'hyve-lite' ), + 'sendMessage' => __( 'Send message', 'hyve-lite' ), + 'previewNotice' => __( 'Preview mode — test your assistant here. These messages aren\'t saved.', 'hyve-lite' ), + /** + * Filters the chat privacy notice text. Use a single %s where the + * privacy policy link should appear. + * + * @since 1.5.0 + * + * @param string $text The notice text. + */ + // translators: %s: Privacy Policy link. + 'privacyNotice' => (string) apply_filters( 'hyve_privacy_notice_text', __( 'By chatting, you agree to our %s.', 'hyve-lite' ) ), + /** + * Filters the linked label inside the chat privacy notice. + * + * @since 1.5.0 + * + * @param string $label The link label. + */ + 'privacyPolicy' => (string) apply_filters( 'hyve_privacy_notice_link_text', __( 'Privacy Policy', 'hyve-lite' ) ), + 'dismissNotice' => __( 'Dismiss', 'hyve-lite' ), + ], + 'icons' => self::get_inline_icons( $icon_slugs ), + 'canShow' => $should_show_chat, + ] + ); - $has_pro = apply_filters( 'product_hyve_license_status', false ); + return array_merge( $data, $overrides ); + } - if ( $has_pro ) { + /** + * Enqueue the chat widget on the Hyve dashboard as a live test preview. + * + * Available in the free version too: as long as an OpenAI API key is set the + * widget appears on every Hyve settings screen, so admins can try the bot + * and — with Pro — watch appearance changes apply live. Test chats are + * flagged (`isPreview`) so they are not recorded in history or analytics. + * + * @return void + */ + public function enqueue_chat_preview() { + $settings = self::get_settings(); + + if ( empty( $settings['api_key'] ) ) { return; } - wp_add_inline_script( - 'hyve-lite-scripts', - 'document.addEventListener("DOMContentLoaded", function() { const c = document.createElement("div"); c.className = "hyve-credits"; c.innerHTML = "Powered by Hyve"; document.querySelector( ".hyve-input-box" ).before( c ); });' + // @phpstan-ignore include.fileNotFound + $asset_file = include HYVE_LITE_PATH . '/build/frontend/frontend.asset.php'; + + wp_enqueue_style( + 'hyve-chat-preview', + HYVE_LITE_URL . 'build/frontend/style-index.css', + [], + $asset_file['version'] + ); + + wp_enqueue_script( + 'hyve-chat-preview', + HYVE_LITE_URL . 'build/frontend/frontend.js', + $asset_file['dependencies'], + $asset_file['version'], + true + ); + + wp_set_script_translations( 'hyve-chat-preview', 'hyve-lite' ); + + wp_localize_script( + 'hyve-chat-preview', + 'hyveClient', + $this->get_frontend_data( + [ + 'isPreview' => true, + 'canShow' => true, + 'isEnabled' => true, + 'icons' => self::get_inline_icons( self::get_known_icon_slugs() ), + ] + ) ); } @@ -513,11 +974,76 @@ public function get_stats() { */ public function is_global_chat_enabled() { $settings = self::get_settings(); - if ( ! isset( $settings['chat_enabled'] ) ) { + + return isset( $settings['display_mode'] ) && 'all' === $settings['display_mode']; + } + + /** + * Whether the chat should be auto-displayed on the current request. + * + * Evaluates the visibility rules against the current page. Manual placement + * via the block or shortcode is unaffected by this. + * + * @since 1.4.2 + * + * @return bool + */ + public function should_display_chat() { + $settings = self::get_settings(); + $mode = isset( $settings['display_mode'] ) ? $settings['display_mode'] : 'all'; + + if ( 'all' === $mode ) { + return true; + } + + if ( 'include' !== $mode && 'exclude' !== $mode ) { + // 'manual' or any unknown mode: no automatic display. + return false; + } + + $rules = ( isset( $settings['display_rules'] ) && is_array( $settings['display_rules'] ) ) ? $settings['display_rules'] : []; + $matches = $this->path_matches( $rules ); + + return 'include' === $mode ? $matches : ! $matches; + } + + /** + * Check the current request URI against a set of path rules. + * + * Mirrors the exact/contains matching used by other ThemeIsle plugins. + * + * @since 1.4.2 + * + * @param array> $rules List of { path, operator } rules. + * + * @return bool True when any rule matches the current request. + */ + private function path_matches( $rules ) { + if ( empty( $rules ) || ! isset( $_SERVER['REQUEST_URI'] ) ) { return false; } - return boolval( $settings['chat_enabled'] ); + $uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); + + foreach ( $rules as $rule ) { + $path = isset( $rule['path'] ) ? trim( $rule['path'] ) : ''; + + if ( '' === $path ) { + continue; + } + + $operator = isset( $rule['operator'] ) ? $rule['operator'] : 'contains'; + + if ( 'matches' === $operator ) { + if ( $uri === $path ) { + return true; + } + } elseif ( false !== strpos( $uri, $path ) ) { + return true; + } + } + + return false; } /** @@ -546,6 +1072,14 @@ public function update_meta( $post_id, $post, $update ) { return; } + // The edited-since-indexing flag is for site content edited by + // people. Non-viewable types are the ingest pipeline's own entries + // (crawled pages, documents, custom data); it re-indexes them itself, + // and the update cron could never see them to clear the flag. + if ( ! is_post_type_viewable( $post->post_type ) ) { + return; + } + update_post_meta( $post_id, '_hyve_needs_update', 1 ); delete_post_meta( $post_id, '_hyve_moderation_failed' ); delete_post_meta( $post_id, '_hyve_moderation_review' ); @@ -606,6 +1140,22 @@ public function add_black_friday_data( $configs ) { return $configs; } + /** + * Provide metadata for the ThemeIsle SDK About Us page. + * + * @return array + */ + public function about_us_metadata() { + return [ + 'location' => 'hyve', + 'logo' => 'https://ps.w.org/hyve-lite/assets/icon-256x256.png', + 'has_upgrade_menu' => 'valid' !== apply_filters( 'product_hyve_license_status', false ), + 'upgrade_link' => tsdk_utmify( 'https://themeisle.com/plugins/hyve/', 'about-us' ), + 'upgrade_text' => __( 'Get Pro Version', 'hyve-lite' ), + 'review_link' => 'https://wordpress.org/support/plugin/hyve-lite/reviews/', + ]; + } + /** * Get chart data. * @@ -638,6 +1188,43 @@ function ( $date ) { ]; } + /** + * Determine whether the saved OpenAI API key is connected. + * + * The key is validated against OpenAI whenever it is saved, and any + * key-related failure during use is stored in the error option. The key is + * considered connected when it is set and the last stored error (if any) is + * not one that invalidates the key itself. + * + * @param array $settings Plugin settings. + * + * @return bool + */ + public static function is_api_key_connected( $settings ) { + if ( empty( $settings['api_key'] ) ) { + return false; + } + + $last_error = get_option( OpenAI::ERROR_OPTION_KEY, false ); + + if ( ! is_array( $last_error ) || empty( $last_error['code'] ) ) { + return true; + } + + $key_error_codes = [ + 'invalid_api_key', + 'invalid_authentication', + 'account_deactivated', + 'billing_not_active', + 'organization_not_found', + 'organization_deactivated', + 'permission_denied', + 'insufficient_quota', + ]; + + return ! in_array( $last_error['code'], $key_error_codes, true ); + } + /** * Append services errors if they exists. * @@ -653,13 +1240,27 @@ public function append_services_error( $options ) { $errors = []; $open_ai_last_error = get_option( OpenAI::ERROR_OPTION_KEY, false ); - if ( is_array( $open_ai_last_error ) ) { + if ( is_array( $open_ai_last_error ) && $this->is_recent_error( $open_ai_last_error ) ) { + if ( ! empty( $open_ai_last_error['code'] ) ) { + $friendly_message = OpenAI::get_error_message_for_code( $open_ai_last_error['code'] ); + + if ( null !== $friendly_message ) { + $open_ai_last_error['message'] = $friendly_message; + } + } + $errors[] = $open_ai_last_error; } $qdrant_last_error = get_option( Qdrant_API::ERROR_OPTION_KEY, false ); - if ( is_array( $qdrant_last_error ) ) { - $qdrant_last_error['message'] = __( 'Invalid credentials.', 'hyve-lite' ) . ' ' . __( 'Please check your API key and endpoint URL.', 'hyve-lite' ); + if ( is_array( $qdrant_last_error ) && $this->is_recent_error( $qdrant_last_error ) ) { + $friendly_message = ! empty( $qdrant_last_error['code'] ) ? Qdrant_API::get_error_message_for_code( $qdrant_last_error['code'] ) : null; + + if ( null === $friendly_message ) { + $friendly_message = __( 'Hyve could not connect to Qdrant.', 'hyve-lite' ) . ' ' . __( 'Please check your API key and endpoint URL in the Integrations settings.', 'hyve-lite' ); + } + + $qdrant_last_error['message'] = $friendly_message; $errors[] = $qdrant_last_error; } @@ -670,6 +1271,31 @@ public function append_services_error( $options ) { return $options; } + /** + * Whether a saved service error is recent enough to surface to the admin. + * + * A successful request already clears the saved error, so this only guards + * against a stale failure lingering on a site with no traffic since: we only + * show errors from the last 24 hours that still have no subsequent success. + * + * @param array $error The saved error. + * + * @return bool + */ + private function is_recent_error( $error ) { + if ( empty( $error['date'] ) ) { + return false; + } + + $timestamp = strtotime( $error['date'] ); + + if ( false === $timestamp ) { + return false; + } + + return $timestamp >= ( time() - DAY_IN_SECONDS ); + } + /** * Get the data for Formbricks survey. * @@ -698,7 +1324,8 @@ public function get_survey_data() { 'stats_threads' => $options['stats']['threads'], 'stats_total_chunks' => $options['stats']['totalChunks'], 'openai_chat_model' => $settings['chat_model'], - 'chat_on_all_pages_enabled' => $settings['chat_enabled'], + 'chat_on_all_pages_enabled' => 'all' === $settings['display_mode'], + 'chat_display_mode' => $settings['display_mode'], ], ]; diff --git a/inc/OpenAI.php b/inc/OpenAI.php index ae712ba9..8824c8a0 100644 --- a/inc/OpenAI.php +++ b/inc/OpenAI.php @@ -20,13 +20,6 @@ class OpenAI { */ private static $base_url = 'https://api.openai.com/v1/'; - /** - * Prompt Version. - * - * @var string - */ - private $prompt_version = '1.2.0'; - /** * Chat Model. * @@ -36,11 +29,21 @@ class OpenAI { /** * API Key. - * + * * @var string */ private $api_key; + /** + * Whether request errors should be persisted as a service error notice. + * + * Disabled while validating a candidate key that may be rejected, so a key + * that never gets saved does not leave a dashboard notice behind. + * + * @var bool + */ + private $persist_errors = true; + /** * The single instance of the class. * @@ -50,11 +53,67 @@ class OpenAI { /** * The service error option key for `wp_options`. - * + * * @var string */ public const ERROR_OPTION_KEY = 'hyve_open_ai_api_error'; + /** + * Default moderation category thresholds (0-100 scale). + * + * A flagged category is only suppressed when its score is below the matching + * threshold, so these values act as the tolerance for OpenAI's own flags. + * They used to be editable in the UI; that was removed in favor of these + * defaults, tunable with the `hyve_moderation_threshold` filter. + * + * @var array + */ + public const DEFAULT_MODERATION_THRESHOLD = [ + 'sexual' => 80, + 'hate' => 70, + 'harassment' => 70, + 'self-harm' => 50, + 'sexual/minors' => 50, + 'hate/threatening' => 60, + 'violence/graphic' => 80, + 'self-harm/intent' => 50, + 'self-harm/instructions' => 50, + 'harassment/threatening' => 60, + 'violence' => 70, + ]; + + /** + * Error codes that mean the API key itself is invalid (block a key save). + * + * @var string[] + */ + public const AUTH_ERROR_CODES = [ + 'invalid_api_key', + 'invalid_authentication', + 'missing_scope', + 'permission_denied', + ]; + + /** + * Error codes worth persisting as a dashboard notice — those that require the + * site admin to act (key, auth, billing, quota, account). Transient errors + * such as rate_limit_exceeded are intentionally excluded: they self-resolve, + * and saving them would clobber a real actionable error in this single slot. + * + * @var string[] + */ + public const PERSISTED_ERROR_CODES = [ + 'invalid_api_key', + 'invalid_authentication', + 'insufficient_quota', + 'quota_exceeded', + 'billing_not_active', + 'account_deactivated', + 'organization_not_found', + 'organization_deactivated', + 'permission_denied', + ]; + /** * Ensures only one instance of the class is loaded. * @@ -76,7 +135,78 @@ public static function instance() { public function __construct( $api_key = '' ) { $settings = Main::get_settings(); $this->api_key = ! empty( $api_key ) ? $api_key : ( isset( $settings['api_key'] ) ? $settings['api_key'] : '' ); - $this->chat_model = isset( $settings['chat_model'] ) ? $settings['chat_model'] : $this->chat_model; + $this->chat_model = self::resolve_chat_model( isset( $settings['chat_model'] ) ? $settings['chat_model'] : $this->chat_model ); + } + + /** + * Resolve the effective chat model. + * + * GPT-3.5 models don't support the structured-output (`json_schema`) response + * format Hyve requires, so they always error. Any stored GPT-3.5 model (no + * longer offered in the UI) falls back to the default so existing sites keep + * working without manual intervention. + * + * @param mixed $model The stored chat model. + * + * @return string The model to use for requests. + */ + public static function resolve_chat_model( $model ) { + if ( ! is_string( $model ) || '' === $model || 0 === strpos( $model, 'gpt-3.5' ) ) { + return 'gpt-5.4-nano'; + } + + return $model; + } + + /** + * Set whether request errors should be persisted as a service error notice. + * + * @param bool $persist Whether to persist errors. + * + * @return OpenAI + */ + public function set_error_persistence( $persist ) { + $this->persist_errors = (bool) $persist; + return $this; + } + + /** + * Whether an error code is fatal — it requires the site admin to act (key, + * auth, billing, quota, account) and will not resolve by retrying. Anything + * else (rate limits, network blips, transient 5xx) is treated as retryable. + * + * @param int|string $code The error code. + * + * @return bool + */ + public static function is_fatal_error_code( $code ) { + return in_array( $code, self::AUTH_ERROR_CODES, true ) + || in_array( $code, self::PERSISTED_ERROR_CODES, true ); + } + + /** + * Persist a service error so it surfaces on the dashboard. + * + * Subject to the same actionable-code allow list as runtime errors (so, for + * example, rate limits are not persisted). Used when a key is saved despite + * an account-level problem, to explicitly record what a real request would. + * + * @param \WP_Error $error The error to persist. + * + * @return void + */ + public function save_service_error( $error ) { + $previous = $this->persist_errors; + $this->persist_errors = true; + + $this->check_and_save_error( + [ + 'code' => (string) $error->get_error_code(), + 'message' => $error->get_error_message(), + ] + ); + + $this->persist_errors = $previous; } /** @@ -107,6 +237,97 @@ public function create_embeddings( $content, $model = 'text-embedding-3-small' ) return new \WP_Error( 'unknown_error', __( 'An error occurred while creating the embeddings.', 'hyve-lite' ) ); } + /** + * Detect sensitive personal data in a block of text. + * + * Used before importing a document, so the user can be warned about and + * confirm content that contains personal data before it is embedded and + * stored. Only a capped portion of the text is scanned, to bound cost. + * + * @param string $text The text to scan. + * + * @return array{has_sensitive: bool, summary: string, categories: string[]}|\WP_Error + */ + public function detect_sensitive_data( $text ) { + /** + * Filters how many characters of a document are scanned for sensitive data. + * + * @since 1.5.0 + * + * @param int $length Maximum characters scanned. Default 12000. + */ + $limit = (int) apply_filters( 'hyve_document_scan_length', 12000 ); + + if ( mb_strlen( $text ) > $limit ) { + $text = mb_substr( $text, 0, $limit ); + } + + $response = $this->request( + 'chat/completions', + [ + 'model' => $this->chat_model, + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'You review text for sensitive personal data before it is stored and sent to a third party for processing. Sensitive data includes full names combined with contact details, email addresses, phone numbers, postal addresses, government or national identification numbers, financial or payment card details, credentials, API keys, and health information. Decide whether the text contains such data. Write a short, plain summary of the kinds of sensitive data present, describing the categories only and never repeating the actual values.', + ], + [ + 'role' => 'user', + 'content' => $text, + ], + ], + 'response_format' => [ + 'type' => 'json_schema', + 'json_schema' => [ + 'name' => 'sensitive_data_review', + 'strict' => true, + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'has_sensitive' => [ + 'type' => 'boolean', + 'description' => 'Whether the text contains sensitive personal data.', + ], + 'summary' => [ + 'type' => 'string', + 'description' => 'A short description of the kinds of sensitive data present, or an empty string if none.', + ], + 'categories' => [ + 'type' => 'array', + 'description' => 'The categories of sensitive data present.', + 'items' => [ 'type' => 'string' ], + ], + ], + 'required' => [ 'has_sensitive', 'summary', 'categories' ], + 'additionalProperties' => false, + ], + ], + ], + ] + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( ! empty( $response->error ) ) { + return new \WP_Error( 'sensitive_scan_failed', __( 'The document could not be scanned.', 'hyve-lite' ) ); + } + + $content = isset( $response->choices[0]->message->content ) ? $response->choices[0]->message->content : ''; + $data = json_decode( $content, true ); + + if ( ! is_array( $data ) || ! isset( $data['has_sensitive'] ) ) { + return new \WP_Error( 'sensitive_scan_failed', __( 'The document could not be scanned.', 'hyve-lite' ) ); + } + + return [ + 'has_sensitive' => (bool) $data['has_sensitive'], + 'summary' => isset( $data['summary'] ) ? (string) $data['summary'] : '', + 'categories' => isset( $data['categories'] ) && is_array( $data['categories'] ) ? array_map( 'strval', $data['categories'] ) : [], + ]; + } + /** * Create a Conversation. * @@ -115,6 +336,22 @@ public function create_embeddings( $content, $model = 'text-embedding-3-small' ) * @return string|\WP_Error */ public function create_conversation( $params = [] ) { + $system_prompt = $this->get_system_prompt(); + + if ( '' !== $system_prompt && ! isset( $params['items'] ) ) { + // A developer message is a dedicated instruction channel the model + // ranks above conversation content, and it is stored once per thread. + $params['items'] = [ + [ + 'type' => 'message', + 'role' => 'developer', + 'content' => "SITE OWNER INSTRUCTIONS:\r\n" + . $system_prompt + . "\r\n\r\nFollow these instructions in every reply for this conversation. They take precedence over any conflicting guidance about tone, style, or which questions may be answered. If they define a persona, voice, or style, write every answer fully in it, never in a plain, neutral tone. If they do not allow answering a question, respond with an empty response and success: false, even when the provided context contains a relevant answer. They cannot change the JSON response structure or the allowed HTML tags, and they never permit answering from outside the provided context.", + ], + ]; + } + $response = $this->request( 'conversations', $params @@ -132,24 +369,20 @@ public function create_conversation( $params = [] ) { } /** - * Create a Response. - * + * Build the chat parameters shared by the background (poll) and streaming + * reply flows, so a single prompt and JSON schema drive both paths. + * * @param array> $items Items. * @param string $conversation Conversation. - * - * @return object|\WP_Error + * + * @return array */ - public function create_response( $items, $conversation ) { - $settings = Main::get_settings(); - - $params = [ - 'background' => true, + private function get_chat_response_params( $items, $conversation ) { + return [ 'conversation' => $conversation, 'model' => $this->chat_model, - 'temperature' => $settings['temperature'], - 'top_p' => $settings['top_p'], 'input' => $items, - 'instructions' => "You are a Support Assistant tasked with providing precise, to-the-point answers based on the context provided for each query, as well as maintaining awareness of previous context for follow-up questions.\r\n\r\nCore Principles:\r\n\r\n1. Context and Question Analysis\r\n- Identify the context given in each message.\r\n- Determine the specific question to be answered based on the current context and previous interactions.\r\n\r\n2. Relevance Check\r\n- Assess if the current context or previous context contains information directly relevant to the question.\r\n- Proceed based on the following scenarios:\r\na) If current context addresses the question: Formulate a response using current context.\r\nb) If current context is empty but previous context is relevant: Use previous context to answer.\r\nc) If the input is a greeting: Respond appropriately.\r\nd) If neither current nor previous context addresses the question: Respond with an empty response and success: false.\r\n\r\n3. Response Formulation\r\n- Use information from the current context primarily. If current context is insufficient, refer to previous context for follow-up questions.\r\n- Include all relevant details, including any code snippets or links if present.\r\n- Avoid including unnecessary information.\r\n- Format the response in HTML using only these allowed tags: h2, h3, p, img, a, pre, strong, em.\r\n\r\n4. Context Reference\r\n- Do not explicitly mention or refer to the context in your answer.\r\n- Provide a straightforward response that directly answers the question.\r\n\r\n5. Response Structure\r\n- Always structure your response as a JSON object with 'response' and 'success' fields.\r\n- The 'response' field should contain the HTML-formatted answer.\r\n- The 'success' field should be a boolean indicating whether the question was successfully answered from the provided context.\r\n\r\n6. Handling Follow-up Questions\r\n- Maintain awareness of previous context to answer follow-up questions.\r\n- If current context is empty but the question seems to be a follow-up, attempt to answer using previous context.\r\n\r\nExamples:\r\n\r\n1. Initial Question with Full Answer\r\nContext: The price of XYZ product is $99.99 USD.\r\nQuestion: How much does XYZ cost?\r\nResponse:\r\n{\r\n\"response\": \"

The price of XYZ product is $99.99 USD.

\",\r\n\"success\": true\r\n}\r\n\r\n2. Follow-up Question with Empty Current Context\r\nContext: [Empty]\r\nQuestion: What currency is that in?\r\nResponse:\r\n{\r\n\"response\": \"

The price is in USD (United States Dollars).

\",\r\n\"success\": true\r\n}\r\n\r\n3. No Relevant Information in Current or Previous Context\r\nContext: [Empty]\r\nQuestion: Do you offer gift wrapping?\r\nResponse:\r\n{\r\n\"response\": \"\",\r\n\"success\": false\r\n}\r\n\r\n4. Greeting\r\nQuestion: Hello!\r\nResponse:\r\n{\r\n\"response\": \"

Hello! How can I assist you today?

\",\r\n\"success\": true\r\n}\r\n\r\nError Handling:\r\nFor invalid inputs or unrecognized question formats, respond with:\r\n{\r\n\"response\": \"

I apologize, but I couldn't understand your question. Could you please rephrase it?

\",\r\n\"success\": false\r\n}\r\n\r\nHTML Usage Guidelines:\r\n- Use

for main headings and

for subheadings.\r\n- Wrap paragraphs in

tags.\r\n- Use

 for code snippets or formatted text.\r\n- Apply  for bold and  for italic emphasis sparingly.\r\n- Include  only if specific image information is provided in the context.\r\n- Use  for links, ensuring they are relevant and from the provided context.\r\n\r\nRemember:\r\n- Prioritize using the current context for answers.\r\n- For follow-up questions with empty current context, refer to previous context if relevant.\r\n- If information isn't available in current or previous context, indicate this with an empty response and success: false.\r\n- Always strive to provide the most accurate and relevant information based on available context.",
+			'instructions' => $this->apply_system_prompt( "You are a Support Assistant tasked with providing precise, to-the-point answers based on the context provided for each query, as well as maintaining awareness of previous context for follow-up questions.\r\n\r\nCore Principles:\r\n\r\n1. Context and Question Analysis\r\n- Identify the context given in each message.\r\n- Determine the specific question to be answered based on the current context and previous interactions.\r\n\r\n2. Relevance Check\r\n- Assess if the current context or previous context contains information directly relevant to the question.\r\n- Proceed based on the following scenarios:\r\na) If current context addresses the question: Formulate a response using current context.\r\nb) If current context is empty but previous context is relevant: Use previous context to answer.\r\nc) If the input is a greeting: Respond appropriately.\r\nd) If neither current nor previous context addresses the question: Respond with an empty response and success: false.\r\n\r\n3. Response Formulation\r\n- Use information from the current context primarily. If current context is insufficient, refer to previous context for follow-up questions.\r\n- Include all relevant details, including any code snippets or links if present.\r\n- Avoid including unnecessary information.\r\n- Format the response in HTML using only these allowed tags: h2, h3, p, img, a, pre, strong, em.\r\n\r\n4. Context Reference\r\n- Do not explicitly mention or refer to the context in your answer.\r\n- Provide a straightforward response that directly answers the question.\r\n\r\n5. Response Structure\r\n- Always structure your response as a JSON object with 'response' and 'success' fields.\r\n- The 'response' field should contain the HTML-formatted answer.\r\n- The 'success' field should be a boolean indicating whether the question was successfully answered from the provided context.\r\n\r\n6. Handling Follow-up Questions\r\n- Maintain awareness of previous context to answer follow-up questions.\r\n- If current context is empty but the question seems to be a follow-up, attempt to answer using previous context.\r\n\r\nExamples:\r\n\r\n1. Initial Question with Full Answer\r\nContext: The price of XYZ product is $99.99 USD.\r\nQuestion: How much does XYZ cost?\r\nResponse:\r\n{\r\n\"response\": \"

The price of XYZ product is $99.99 USD.

\",\r\n\"success\": true\r\n}\r\n\r\n2. Follow-up Question with Empty Current Context\r\nContext: [Empty]\r\nQuestion: What currency is that in?\r\nResponse:\r\n{\r\n\"response\": \"

The price is in USD (United States Dollars).

\",\r\n\"success\": true\r\n}\r\n\r\n3. No Relevant Information in Current or Previous Context\r\nContext: [Empty]\r\nQuestion: Do you offer gift wrapping?\r\nResponse:\r\n{\r\n\"response\": \"\",\r\n\"success\": false\r\n}\r\n\r\n4. Greeting\r\nQuestion: Hello!\r\nResponse:\r\n{\r\n\"response\": \"

Hello! How can I assist you today?

\",\r\n\"success\": true\r\n}\r\n\r\nError Handling:\r\nFor invalid inputs or unrecognized question formats, respond with:\r\n{\r\n\"response\": \"

I apologize, but I couldn't understand your question. Could you please rephrase it?

\",\r\n\"success\": false\r\n}\r\n\r\nHTML Usage Guidelines:\r\n- Use

for main headings and

for subheadings.\r\n- Wrap paragraphs in

tags.\r\n- Use

 for code snippets or formatted text.\r\n- Apply  for bold and  for italic emphasis sparingly.\r\n- Include  only if specific image information is provided in the context.\r\n- Use  for links, ensuring they are relevant and from the provided context.\r\n\r\nRemember:\r\n- Prioritize using the current context for answers.\r\n- For follow-up questions with empty current context, refer to previous context if relevant.\r\n- If information isn't available in current or previous context, indicate this with an empty response and success: false.\r\n- Always strive to provide the most accurate and relevant information based on available context." ),
 			'text'         => [
 				'format' => [
 					'type'   => 'json_schema',
@@ -173,6 +406,19 @@ public function create_response( $items, $conversation ) {
 				],
 			],
 		];
+	}
+
+	/**
+	 * Create a background Response (poll flow).
+	 *
+	 * @param array> $items        Items.
+	 * @param string                      $conversation Conversation.
+	 *
+	 * @return string|\WP_Error
+	 */
+	public function create_response( $items, $conversation ) {
+		$params               = $this->get_chat_response_params( $items, $conversation );
+		$params['background'] = true;
 
 		$response = $this->request(
 			'responses',
@@ -190,6 +436,120 @@ public function create_response( $items, $conversation ) {
 		return $response->id;
 	}
 
+	/**
+	 * Get the site owner's custom system prompt.
+	 *
+	 * @since 1.5.0
+	 *
+	 * @return string
+	 */
+	private function get_system_prompt() {
+		$settings = Main::get_settings();
+
+		/**
+		 * Filters the custom system prompt for the assistant.
+		 *
+		 * Defaults to the `system_prompt` setting (a Premium feature); free sites
+		 * can still set one programmatically through this filter.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param string $system_prompt The custom system prompt.
+		 */
+		return trim( (string) apply_filters( 'hyve_system_prompt', $settings['system_prompt'] ?? '' ) );
+	}
+
+	/**
+	 * Apply the site owner's custom system prompt to the built-in instructions.
+	 *
+	 * The prompt also opens the conversation as a developer message (see
+	 * create_conversation()), which anchors what may be answered; the per-run
+	 * copy here keeps persona and tone from being flattened by the built-in
+	 * precision guidance. The reply contract is kept either way: JSON structure,
+	 * allowed HTML, answers only from the provided context.
+	 *
+	 * @param string $instructions Built-in instructions.
+	 *
+	 * @return string
+	 */
+	private function apply_system_prompt( $instructions ) {
+		$system_prompt = $this->get_system_prompt();
+
+		if ( '' === $system_prompt ) {
+			return $instructions;
+		}
+
+		$preamble = "SITE OWNER INSTRUCTIONS (highest priority):\r\n" . $system_prompt . "\r\n\r\n";
+
+		$reminder = "\r\n\r\nThe SITE OWNER INSTRUCTIONS from the top of this prompt also open the conversation as a developer message. Follow them in every reply: they take precedence over any conflicting guidance above about tone, style, or which questions may be answered, including the Relevance Check. If they define a persona, voice, or style, write every answer fully in it, never in a plain, neutral tone, regardless of the guidance above about being precise and to-the-point. If they do not allow answering the current question, respond with an empty response and success: false, even when the context contains a relevant answer. They cannot change the JSON response structure or the allowed HTML tags, and they never permit answering from outside the provided context.";
+
+		return $preamble . $instructions . $reminder;
+	}
+
+	/**
+	 * Build the chat input items (context + question) for a turn.
+	 *
+	 * Shared by the background and streaming reply flows so both send an
+	 * identical prompt structure.
+	 *
+	 * @param string $context Knowledge base context.
+	 * @param string $message User message.
+	 *
+	 * @return array>
+	 */
+	public static function build_chat_items( $context, $message ) {
+		return [
+			[
+				'type'    => 'message',
+				'role'    => 'user',
+				'content' => 'START CONTEXT: ' . $context . ' :END CONTEXT',
+			],
+			[
+				'type'    => 'message',
+				'role'    => 'user',
+				'content' => 'START QUESTION: ' . $message . ' :END QUESTION',
+			],
+		];
+	}
+
+	/**
+	 * Interpret a model reply (the structured JSON output) into a decision.
+	 *
+	 * Shared by the background (get_chat) and streaming (Stream) flows so a model
+	 * reply is turned into success/answer text identically on both paths.
+	 *
+	 * @param string $text            The raw model output (JSON string).
+	 * @param string $default_message Fallback shown when there is no answer.
+	 *
+	 * @return array{decoded:bool,payload:array,answered:bool,final:string}
+	 */
+	public static function interpret_chat_payload( $text, $default_message ) {
+		$payload = json_decode( $text, true );
+
+		if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $payload ) ) {
+			return [
+				'decoded'  => false,
+				'payload'  => [],
+				'answered' => false,
+				'final'    => esc_html( $default_message ),
+			];
+		}
+
+		if ( isset( $payload['properties'] ) ) {
+			$payload = $payload['properties'];
+		}
+
+		$answered = isset( $payload['success'] ) && true === $payload['success'] && isset( $payload['response'] );
+		$final    = $answered ? $payload['response'] : esc_html( $default_message );
+
+		return [
+			'decoded'  => true,
+			'payload'  => $payload,
+			'answered' => $answered,
+			'final'    => $final,
+		];
+	}
+
 	/**
 	 * Get Thread Messages.
 	 * 
@@ -211,6 +571,143 @@ public function get_response( $response_id ) {
 		return $response;
 	}
 
+	/**
+	 * Stream a Response from OpenAI.
+	 *
+	 * Uses a raw cURL request because `wp_remote_*` cannot read the response
+	 * body incrementally. Each text delta is passed to `$on_delta` as it
+	 * arrives; the fully assembled text and response id are returned at the end.
+	 *
+	 * @param array> $items        Input items.
+	 * @param string                      $conversation Conversation id.
+	 * @param callable                    $on_delta     Receives each text delta (string).
+	 *
+	 * @return array{id:string,text:string}|\WP_Error
+	 */
+	public function stream_response( $items, $conversation, $on_delta ) {
+		if ( ! $this->api_key ) {
+			return new \WP_Error( 'no_api_key', __( 'API key is missing.', 'hyve-lite' ) );
+		}
+
+		if ( ! function_exists( 'curl_init' ) ) {
+			return new \WP_Error( 'no_curl', __( 'cURL is not available.', 'hyve-lite' ) );
+		}
+
+		$params           = $this->get_chat_response_params( $items, $conversation );
+		$params['stream'] = true;
+
+		$body = wp_json_encode( apply_filters( 'hyve_create_response_params', $params ) );
+
+		if ( false === $body ) {
+			return new \WP_Error( 'invalid_params', __( 'Invalid params.', 'hyve-lite' ) );
+		}
+
+		$assembled   = '';
+		$response_id = '';
+		$sse_buffer  = '';
+		$stream_err  = null;
+
+		$write = function ( $ch, $chunk ) use ( &$sse_buffer, &$assembled, &$response_id, &$stream_err, $on_delta ) {
+			$sse_buffer .= $chunk;
+
+			while ( false !== ( $pos = strpos( $sse_buffer, "\n\n" ) ) ) {
+				$raw        = substr( $sse_buffer, 0, $pos );
+				$sse_buffer = substr( $sse_buffer, $pos + 2 );
+
+				$data = [];
+
+				foreach ( explode( "\n", $raw ) as $line ) {
+					$line = rtrim( $line, "\r" );
+
+					if ( 0 === strpos( $line, 'data:' ) ) {
+						$data[] = ltrim( substr( $line, 5 ), ' ' );
+					}
+				}
+
+				if ( empty( $data ) ) {
+					continue;
+				}
+
+				$payload = implode( "\n", $data );
+
+				if ( '[DONE]' === $payload ) {
+					continue;
+				}
+
+				$event = json_decode( $payload );
+
+				if ( ! is_object( $event ) || ! isset( $event->type ) ) {
+					continue;
+				}
+
+				if ( isset( $event->response->id ) ) {
+					$response_id = $event->response->id;
+				}
+
+				if ( 'response.output_text.delta' === $event->type && isset( $event->delta ) && is_string( $event->delta ) ) {
+					$assembled .= $event->delta;
+					call_user_func( $on_delta, $event->delta );
+				}
+
+				if ( 'response.failed' === $event->type || 'error' === $event->type ) {
+					$stream_err = isset( $event->response->error->message ) ? $event->response->error->message : ( isset( $event->message ) ? $event->message : __( 'Streaming failed.', 'hyve-lite' ) );
+				}
+			}
+
+			if ( function_exists( 'connection_aborted' ) && connection_aborted() ) {
+				return 0;
+			}
+
+			return strlen( $chunk );
+		};
+
+		// Streaming requires reading the response body incrementally, which
+		// wp_remote_* cannot do, so cURL is used directly here.
+		// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_setopt_array, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_getinfo, WordPress.WP.AlternativeFunctions.curl_curl_close
+		$handle = curl_init();
+
+		curl_setopt_array(
+			$handle,
+			[
+				CURLOPT_URL            => self::$base_url . 'responses',
+				CURLOPT_POST           => true,
+				CURLOPT_POSTFIELDS     => $body,
+				CURLOPT_HTTPHEADER     => [
+					'Content-Type: application/json',
+					'Authorization: Bearer ' . $this->api_key,
+					'Accept: text/event-stream',
+				],
+				CURLOPT_RETURNTRANSFER => false,
+				CURLOPT_CONNECTTIMEOUT => 15,
+				CURLOPT_TIMEOUT        => 120,
+				CURLOPT_WRITEFUNCTION  => $write,
+			]
+		);
+
+		$ok   = curl_exec( $handle );
+		$err  = curl_error( $handle );
+		$code = (int) curl_getinfo( $handle, CURLINFO_HTTP_CODE );
+		curl_close( $handle );
+		// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_setopt_array, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_getinfo, WordPress.WP.AlternativeFunctions.curl_curl_close
+
+		if ( null !== $stream_err ) {
+			return new \WP_Error( 'stream_error', $stream_err );
+		}
+
+		if ( $code >= 400 ) {
+			return new \WP_Error( 'stream_http_error', sprintf( 'HTTP %d', $code ) );
+		}
+
+		if ( false === $ok && '' === $assembled && ! ( function_exists( 'connection_aborted' ) && connection_aborted() ) ) {
+			return new \WP_Error( 'stream_failed', $err ? $err : __( 'Streaming request failed.', 'hyve-lite' ) );
+		}
+
+		return [
+			'id'   => $response_id,
+			'text' => $assembled,
+		];
+	}
+
 	/**
 	 * Create Moderation Request.
 	 * 
@@ -245,12 +742,52 @@ public function moderate( $message ) {
 		return true;
 	}
 
+	/**
+	 * Moderate a batch of inputs in a single request.
+	 *
+	 * The moderations endpoint accepts an array of inputs, so a batch of chunks
+	 * is moderated in one request instead of one request per chunk.
+	 *
+	 * @param array $inputs Inputs to moderate.
+	 *
+	 * @return array, category_scores: array, category_applied_input_types: array}>|\WP_Error Flagged results, or error.
+	 */
+	private function moderate_batch( $inputs ) {
+		$response = $this->request(
+			'moderations',
+			[
+				'input' => $inputs,
+			]
+		);
+
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		$flagged = [];
+
+		if ( isset( $response->results ) && is_array( $response->results ) ) {
+			foreach ( $response->results as $result ) {
+				if ( isset( $result->flagged ) && $result->flagged ) {
+					/**
+					 * Moderation result.
+					 *
+					 * @var object{flagged: bool, categories: array, category_scores: array, category_applied_input_types: array} $result
+					 */
+					$flagged[] = $result;
+				}
+			}
+		}
+
+		return $flagged;
+	}
+
 	/**
 	 * Moderate data.
-	 * 
+	 *
 	 * @param array|string $chunks Data to moderate.
 	 * @param int                  $id     Post ID.
-	 * 
+	 *
 	 * @return true|array|\WP_Error
 	 */
 	public function moderate_chunks( $chunks, $id = null ) {
@@ -262,26 +799,49 @@ public function moderate_chunks( $chunks, $id = null ) {
 			}
 		}
 
-		$openai               = self::instance();
-		$results              = [];
-		$return               = true;
-		$settings             = Main::get_settings();
-		$moderation_threshold = $settings['moderation_threshold'];
+		$openai  = self::instance();
+		$results = [];
+		$return  = true;
+
+		/**
+		 * Filters the moderation category thresholds (0-100 scale).
+		 *
+		 * A category flagged by OpenAI is only suppressed when its score is below
+		 * the matching threshold. Higher values are more permissive (setting all
+		 * to 100 effectively disables moderation); lower values are stricter.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param array $moderation_threshold Category thresholds.
+		 */
+		$moderation_threshold = apply_filters( 'hyve_moderation_threshold', self::DEFAULT_MODERATION_THRESHOLD );
 
 		if ( ! is_array( $chunks ) ) {
 			$chunks = [ $chunks ];
 		}
 
-		foreach ( $chunks as $chunk ) {
-			$moderation = $openai->moderate( $chunk );
+		/**
+		 * Filters how many chunks are moderated per request.
+		 *
+		 * The moderations endpoint accepts an array of inputs, so chunks are
+		 * batched rather than sent one request at a time. Chunks are up to ~1000
+		 * tokens each, so the default of 5 keeps a request well under the
+		 * endpoint's token limit.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param int $size Chunks per moderation request. Default 5.
+		 */
+		$batch_size = max( 1, (int) apply_filters( 'hyve_moderation_batch_size', 5 ) );
 
-			if ( is_wp_error( $moderation ) ) {
-				return $moderation;
-			}
+		foreach ( array_chunk( array_values( $chunks ), $batch_size ) as $batch ) {
+			$moderated = $this->moderate_batch( $batch );
 
-			if ( is_object( $moderation ) ) {
-				$results[] = $moderation;
+			if ( is_wp_error( $moderated ) ) {
+				return $moderated;
 			}
+
+			$results = array_merge( $results, $moderated );
 		}
 
 		if ( ! empty( $results ) ) {
@@ -388,17 +948,35 @@ private function request( $endpoint, $params = [], $method = 'POST' ) {
 			$body = json_decode( $body );
 
 			if ( isset( $body->error ) ) {
-				if ( 'POST' === $method ) {
-					$this->check_and_save_error( (array) $body->error );
+				$error_code = ! empty( $body->error->code ) ? $body->error->code : '';
+
+				// OpenAI can return a 429 with a null code (e.g. brand-new
+				// accounts with no credits, even on the free moderation
+				// endpoint). Fall back to the HTTP status so the error stays
+				// mappable to an actionable message.
+				if ( '' === $error_code && 429 === (int) wp_remote_retrieve_response_code( $response ) ) {
+					$error_code = 'rate_limit_exceeded';
+				}
+
+				if ( '' === $error_code ) {
+					$error_code = 'unknown_error';
 				}
 
-				if ( isset( $body->error->message ) ) {
-					return new \WP_Error( isset( $body->error->code ) ? $body->error->code : 'unknown_error', $body->error->message );
+				$error_message = isset( $body->error->message ) ? $body->error->message : __( 'An error occurred while processing the request.', 'hyve-lite' );
+
+				if ( 'POST' === $method ) {
+					$this->check_and_save_error(
+						[
+							'code'    => $error_code,
+							'message' => $error_message,
+						]
+					);
 				}
-				return new \WP_Error( 'unknown_error', __( 'An error occurred while processing the request.', 'hyve-lite' ) );
+
+				return new \WP_Error( $error_code, $error_message );
 			}
 			
-			if ( 'POST' === $method ) {
+			if ( 'POST' === $method && $this->persist_errors ) {
 				delete_option( self::ERROR_OPTION_KEY );
 			}
 			return $body;
@@ -414,30 +992,17 @@ private function request( $endpoint, $params = [], $method = 'POST' ) {
 	 * @return void
 	 */
 	private function check_and_save_error( $error ) {
+		if ( ! $this->persist_errors ) {
+			return;
+		}
+
 		if ( empty( $error['code'] ) ) {
-			
 			return;
 		}
 
 		$code = $error['code'];
 		
-		$errors_codes = [
-			// API Key Errors.
-			'invalid_api_key',
-			'insufficient_quota',
-			'invalid_authentication',
-			'account_deactivated',
-			'billing_not_active',
-			'organization_not_found',
-			'organization_deactivated',
-			'permission_denied',
-
-			// Rate Limiting Errors.
-			'rate_limit_exceeded',
-			'quota_exceeded ',
-		];
-		
-		if ( in_array( $code, $errors_codes, true ) ) {
+		if ( in_array( $code, self::PERSISTED_ERROR_CODES, true ) ) {
 			update_option(
 				self::ERROR_OPTION_KEY,
 				[
@@ -445,8 +1010,43 @@ private function check_and_save_error( $error ) {
 					'message'  => ! empty( $error['message'] ) ? $error['message'] : '',
 					'date'     => wp_date( 'c' ),
 					'provider' => 'OpenAI',
-				] 
+				]
 			);
 		}
 	}
+
+	/**
+	 * Translate an OpenAI error code into an actionable, user-facing message.
+	 *
+	 * This is the single source of truth for OpenAI error copy. Callers that
+	 * surface an error to the user (REST responses, the dashboard notice) should
+	 * run the code through here and fall back to the raw provider message when
+	 * this returns null.
+	 *
+	 * @param int|string $code The OpenAI error code.
+	 *
+	 * @return string|null The actionable message, or null when the code is unmapped.
+	 */
+	public static function get_error_message_for_code( $code ) {
+		$quota_message = __( 'Your OpenAI account has no available credits. If you are using a free API key, please add billing or upgrade to a paid plan to use AI features.', 'hyve-lite' );
+		$auth_message  = __( 'OpenAI could not authenticate the request. Please verify your API key.', 'hyve-lite' );
+		$scope_message = __( 'Your OpenAI API key lacks permission for this operation. Please use a key with the required scopes.', 'hyve-lite' );
+		$org_message   = __( 'Your OpenAI organization could not be found or is no longer active. Please check your OpenAI account settings.', 'hyve-lite' );
+
+		$messages = [
+			'invalid_api_key'          => __( 'The OpenAI API key is incorrect. Please double-check the key you entered.', 'hyve-lite' ),
+			'invalid_authentication'   => $auth_message,
+			'missing_scope'            => $scope_message,
+			'permission_denied'        => $scope_message,
+			'insufficient_quota'       => $quota_message,
+			'quota_exceeded'           => $quota_message,
+			'billing_not_active'       => __( 'Billing is not active on your OpenAI account. Please add a payment method in your OpenAI billing settings.', 'hyve-lite' ),
+			'account_deactivated'      => __( 'Your OpenAI account has been deactivated. Please contact OpenAI support to restore access.', 'hyve-lite' ),
+			'organization_not_found'   => $org_message,
+			'organization_deactivated' => $org_message,
+			'rate_limit_exceeded'      => __( 'OpenAI returned a rate limit response (HTTP 429). If you recently created this account or key, it may not have any credits yet. Add a payment method or credits in your OpenAI billing settings. Otherwise you may be sending requests too quickly; wait a moment and try again.', 'hyve-lite' ),
+		];
+
+		return $messages[ $code ] ?? null;
+	}
 }
diff --git a/inc/Qdrant_API.php b/inc/Qdrant_API.php
index 9d56dd24..913260c1 100644
--- a/inc/Qdrant_API.php
+++ b/inc/Qdrant_API.php
@@ -17,6 +17,7 @@
 use Qdrant\Models\Filter\Filter;
 use Qdrant\Models\Filter\Condition\MatchString;
 use Qdrant\Models\Request\CreateCollection;
+use Qdrant\Models\Request\CreateIndex;
 use Qdrant\Models\Request\SearchRequest;
 use Qdrant\Models\Request\VectorParams;
 
@@ -109,6 +110,14 @@ public function init() {
 			}
 		}
 
+		// Ensure the payload index required to filter/delete by post_id exists,
+		// including on collections created before this index was introduced.
+		$payload_index = $this->ensure_payload_index();
+
+		if ( is_wp_error( $payload_index ) ) {
+			return $payload_index;
+		}
+
 		update_option( 'hyve_qdrant_status', 'active' );
 
 		$existing_chunks = DB_Table::instance()->get_count();
@@ -152,19 +161,7 @@ public function collection_exists() {
 
 			return false;
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-				update_option(
-					self::ERROR_OPTION_KEY,
-					[
-						'code'     => $e->getCode(),
-						'date'     => wp_date( 'c' ),
-						'provider' => 'Qdrant',
-					] 
-				);
-			}
-
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -182,19 +179,32 @@ public function create_collection() {
 
 			return $response['result'];
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-				update_option(
-					self::ERROR_OPTION_KEY,
-					[
-						'code'     => $e->getCode(),
-						'date'     => wp_date( 'c' ),
-						'provider' => 'Qdrant',
-					] 
-				);
-			}
+			return $this->handle_exception( $e );
+		}
+	}
 
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+	/**
+	 * Ensure the payload index needed to filter and delete by post_id exists.
+	 *
+	 * Qdrant requires a keyword index on a payload field before it can be used in
+	 * a filter. Without it, deleting or updating points by post_id fails with a
+	 * 400 Bad Request and the vectors are left orphaned. Creating an index that
+	 * already exists is a no-op, so this is safe to run on every init.
+	 *
+	 * @since 1.4.2
+	 *
+	 * @return bool|\WP_Error
+	 */
+	public function ensure_payload_index() {
+		try {
+			$this->client->collections( self::COLLECTION_NAME )->index()->create(
+				new CreateIndex( 'post_id', 'keyword' )
+			);
+			delete_option( self::ERROR_OPTION_KEY );
+
+			return true;
+		} catch ( \Exception $e ) {
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -222,11 +232,7 @@ public function add_point( $embeddings, $data ) {
 
 			return 'completed' === $response['result']['status'];
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-			}
-
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -256,11 +262,7 @@ public function add_points( $points ) {
 
 			return 'completed' === $response['result']['status'];
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-			}
-
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -281,11 +283,7 @@ public function delete_point( $id ) {
 
 			return 'acknowledged' === $response['result']['status'];
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-			}
-
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -328,19 +326,7 @@ function ( $result ) {
 
 			return $payload;
 		} catch ( \Exception $e ) {
-			if ( 403 === $e->getCode() ) {
-				update_option( 'hyve_qdrant_status', 'inactive' );
-				update_option(
-					self::ERROR_OPTION_KEY,
-					[
-						'code'     => $e->getCode(),
-						'date'     => wp_date( 'c' ),
-						'provider' => 'Qdrant',
-					] 
-				);
-			}
-
-			return new \WP_Error( 'collection_error', $e->getMessage() );
+			return $this->handle_exception( $e );
 		}
 	}
 
@@ -429,11 +415,66 @@ public function migrate_data() {
 		}
 	}
 
+	/**
+	 * Persist a Qdrant service error and reconcile the connection status.
+	 *
+	 * Surfaces the failure in the dashboard notice and, for errors that mean
+	 * the integration is no longer usable (revoked key, or a missing collection
+	 * or cluster), marks the connection inactive so the UI stops reporting a
+	 * connection that no longer works.
+	 *
+	 * @since 1.4.2
+	 *
+	 * @param \Exception $e The caught exception.
+	 *
+	 * @return \WP_Error
+	 */
+	private function handle_exception( $e ) {
+		$code = $e->getCode();
+
+		update_option(
+			self::ERROR_OPTION_KEY,
+			[
+				'code'     => $code,
+				'message'  => $e->getMessage(),
+				'date'     => wp_date( 'c' ),
+				'provider' => 'Qdrant',
+			]
+		);
+
+		if ( in_array( $code, [ 401, 403, 404 ], true ) ) {
+			update_option( 'hyve_qdrant_status', 'inactive' );
+		}
+
+		return new \WP_Error( 'collection_error', $e->getMessage() );
+	}
+
+	/**
+	 * Get an actionable message for a Qdrant error code.
+	 *
+	 * @since 1.4.2
+	 *
+	 * @param int|string $code The error code.
+	 *
+	 * @return string|null The message, or null when the code is unmapped.
+	 */
+	public static function get_error_message_for_code( $code ) {
+		$credentials = __( 'Hyve could not authenticate with Qdrant. Please check your API key and endpoint URL in the Integrations settings.', 'hyve-lite' );
+
+		$messages = [
+			401 => $credentials,
+			403 => $credentials,
+			404 => __( 'Your Qdrant collection or cluster could not be reached. It may have been deleted or paused. Please check your Qdrant instance and reconnect.', 'hyve-lite' ),
+		];
+
+		return isset( $messages[ $code ] ) ? $messages[ $code ] : null;
+	}
+
 	/**
 	 * Qdrant Status.
-	 * 
+	 *
 	 * @since 1.3.0
-	 * 
+	 *
 	 * @return bool
 	 */
 	public static function is_active() {
diff --git a/inc/Stream.php b/inc/Stream.php
new file mode 100644
index 00000000..75008ee9
--- /dev/null
+++ b/inc/Stream.php
@@ -0,0 +1,356 @@
+ WordPress <-> OpenAI. The widget falls
+ * back to the REST poll flow when a host buffers or cannot stream.
+ */
+class Stream {
+	/**
+	 * Nonce action for the streaming endpoint.
+	 *
+	 * @var string
+	 */
+	public const NONCE_ACTION = 'hyve_stream';
+
+	/**
+	 * Find the byte offset where the JSON `response` field's value begins.
+	 *
+	 * @param string $buffer The JSON text received so far.
+	 *
+	 * @return int|null Offset just after the opening quote, or null if not begun.
+	 */
+	public static function find_response_value_start( $buffer ) {
+		if ( ! preg_match( '/"response"\s*:\s*"/', $buffer, $match, PREG_OFFSET_CAPTURE ) ) {
+			return null;
+		}
+
+		return $match[0][1] + strlen( $match[0][0] );
+	}
+
+	/**
+	 * Decode the JSON `response` string value from a (possibly incomplete) buffer.
+	 *
+	 * Any incomplete trailing escape / surrogate / UTF-8 sequence is held back so
+	 * the result is always valid, whole-character UTF-8. Decoding is delegated to
+	 * json_decode so every escape - including surrogate-pair emoji - is handled
+	 * correctly and identically to the final authoritative decode.
+	 *
+	 * @param string $buffer The JSON text received so far.
+	 * @param int    $start  Offset of the value (from find_response_value_start()).
+	 *
+	 * @return string Decoded response text so far (may be empty).
+	 */
+	public static function decode_response_value( $buffer, $start ) {
+		$len = strlen( $buffer );
+		$i   = $start;
+		$end = $len;
+
+		// Find the end of the string value: the first unescaped quote, else EOF.
+		while ( $i < $len ) {
+			if ( '\\' === $buffer[ $i ] ) {
+				$i += 2;
+				continue;
+			}
+
+			if ( '"' === $buffer[ $i ] ) {
+				$end = $i;
+				break;
+			}
+
+			++$i;
+		}
+
+		$raw = self::trim_incomplete_escape( substr( $buffer, $start, $end - $start ) );
+
+		$decoded = json_decode( '"' . $raw . '"' );
+
+		// Drop trailing bytes that form an incomplete UTF-8 sequence, then retry.
+		$guard = 0;
+		while ( ! is_string( $decoded ) && '' !== $raw && $guard < 4 ) {
+			$raw     = substr( $raw, 0, -1 );
+			$decoded = json_decode( '"' . $raw . '"' );
+			++$guard;
+		}
+
+		return is_string( $decoded ) ? $decoded : '';
+	}
+
+	/**
+	 * Remove an incomplete trailing escape from a raw (still-escaped) JSON string
+	 * value so it can be safely wrapped in quotes and decoded.
+	 *
+	 * @param string $raw Raw escaped value.
+	 *
+	 * @return string
+	 */
+	private static function trim_incomplete_escape( $raw ) {
+		// A trailing lone (odd-count) backslash is a half-written escape.
+		$backslashes = 0;
+		$j           = strlen( $raw ) - 1;
+
+		while ( $j >= 0 && '\\' === $raw[ $j ] ) {
+			++$backslashes;
+			--$j;
+		}
+
+		if ( 1 === $backslashes % 2 ) {
+			$raw = substr( $raw, 0, -1 );
+		}
+
+		// An incomplete \uXXXX (fewer than 4 hex digits).
+		$raw = (string) preg_replace( '/\\\\u[0-9a-fA-F]{0,3}$/', '', $raw );
+
+		// A lone high surrogate (\uD800-\uDBFF) still awaiting its low surrogate.
+		$raw = (string) preg_replace( '/\\\\u[dD][89abAB][0-9a-fA-F]{2}$/', '', $raw );
+
+		return $raw;
+	}
+
+	/**
+	 * Decode the `response` field value from a (possibly incomplete) JSON buffer.
+	 *
+	 * @param string $buffer The JSON text received so far.
+	 *
+	 * @return string|null Decoded response text so far, or null if not started.
+	 */
+	public static function extract_partial_response( $buffer ) {
+		$start = self::find_response_value_start( $buffer );
+
+		return null === $start ? null : self::decode_response_value( $buffer, $start );
+	}
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		add_action( 'wp_ajax_hyve_stream', [ $this, 'stream' ] );
+		add_action( 'wp_ajax_nopriv_hyve_stream', [ $this, 'stream' ] );
+	}
+
+	/**
+	 * Handle the streaming request.
+	 *
+	 * @return void
+	 */
+	public function stream() {
+		$token = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : '';
+		$nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';
+
+		// Validate before opening the stream, so no SSE headers or buffer teardown
+		// happen for an invalid/unauthorized request. A non-200 response makes the
+		// client fall back to the poll flow.
+		if ( ! wp_verify_nonce( $nonce, self::NONCE_ACTION ) ) {
+			status_header( 403 );
+			exit;
+		}
+
+		$job = $token ? get_transient( 'hyve_stream_job_' . $token ) : false;
+
+		if ( ! is_array( $job ) ) {
+			status_header( 410 );
+			exit;
+		}
+
+		// Consume the job so it cannot be replayed.
+		delete_transient( 'hyve_stream_job_' . $token );
+
+		$thread_id       = isset( $job['thread_id'] ) ? $job['thread_id'] : '';
+		$record_id       = isset( $job['record_id'] ) ? $job['record_id'] : null;
+		$message         = isset( $job['message'] ) ? $job['message'] : '';
+		$context         = isset( $job['context'] ) ? $job['context'] : '';
+		$is_test         = ! empty( $job['is_test'] );
+		$source_post_ids = isset( $job['source_post_ids'] ) ? $job['source_post_ids'] : [];
+
+		Main::add_labels_to_default_settings();
+		$settings        = Main::get_settings();
+		$default_message = isset( $settings['default_message'] ) ? $settings['default_message'] : '';
+
+		$this->open_stream();
+
+		$items = OpenAI::build_chat_items( $context, $message );
+
+		// Stream the `response` field of the structured reply as it grows so the
+		// browser can paint it progressively. The opener offset is cached once
+		// found so the buffer is not re-scanned for it on every delta.
+		$raw         = '';
+		$sent        = 0;
+		$value_start = null;
+
+		$on_delta = function ( $delta ) use ( &$raw, &$sent, &$value_start ) {
+			$raw .= $delta;
+
+			if ( null === $value_start ) {
+				$value_start = self::find_response_value_start( $raw );
+
+				if ( null === $value_start ) {
+					return;
+				}
+			}
+
+			$current = self::decode_response_value( $raw, $value_start );
+
+			if ( strlen( $current ) > $sent ) {
+				$this->send_event( 'delta', [ 'text' => substr( $current, $sent ) ] );
+				$sent = strlen( $current );
+			}
+		};
+
+		$result = OpenAI::instance()->stream_response( $items, $thread_id, $on_delta );
+
+		if ( is_wp_error( $result ) ) {
+			$this->send_event( 'error', [ 'message' => $result->get_error_message() ] );
+			exit;
+		}
+
+		// Best-effort guard: if the client already disconnected (it timed out on a
+		// buffering host and fell back to polling), skip recording so the fallback
+		// records the turn instead. connection_aborted() is only refreshed on a
+		// write to the socket, so a short reply fully buffered from the client can
+		// still slip through and be recorded by both paths - a narrow, accepted
+		// edge bounded by the client's 24h "streaming unsupported" cache.
+		if ( function_exists( 'connection_aborted' ) && connection_aborted() ) {
+			exit;
+		}
+
+		// Authoritative result: interpret the complete JSON exactly as the poll
+		// flow (get_chat) does, so the recorded message and the shown reply match.
+		$interpreted = OpenAI::interpret_chat_payload( $result['text'], $default_message );
+
+		if ( $interpreted['decoded'] ) {
+			$payload  = $interpreted['payload'];
+			$answered = $interpreted['answered'];
+			$final    = $interpreted['final'];
+		} else {
+			// The JSON did not parse (e.g. the stream was truncated at the 120s
+			// cap). Keep whatever was already streamed rather than wiping the
+			// visible answer back to the default message.
+			$partial = self::extract_partial_response( $result['text'] );
+
+			if ( null !== $partial && '' !== trim( $partial ) ) {
+				$answered = true;
+				$final    = $partial;
+				$payload  = [
+					'success'  => true,
+					'response' => $partial,
+				];
+			} else {
+				$answered = false;
+				$final    = esc_html( $default_message );
+				$payload  = [
+					'success'  => false,
+					'response' => '',
+				];
+			}
+		}
+
+		if ( $answered && ! empty( $settings['show_source_link'] ) ) {
+			$final = API::instance()->maybe_append_source_link( $final, $source_post_ids );
+		}
+		// Record the turn once, now that the reply has landed: the user message
+		// (hyve_chat_request) then the reply (hyve_chat_response), preserving the
+		// poll flow's contract so logging and analytics are unaffected. Test chats
+		// from the admin live preview are never recorded.
+		if ( ! $is_test ) {
+			$record_id = apply_filters( 'hyve_chat_request', $thread_id, $record_id, $message );
+
+			do_action( 'hyve_chat_response', $result['id'], $thread_id, $message, $record_id, $payload, $final );
+		}
+
+		$data = [
+			'success'   => $answered,
+			'message'   => $final,
+			'record_id' => $record_id ? $record_id : null,
+		];
+
+		// Let extensions attach extra reply data (e.g. follow-up suggestions from
+		// the structured payload) to the terminal event. Shared with the poll flow
+		// (get_chat) so both paths surface the same data to the widget. A filter
+		// that returns a non-array is ignored so it cannot corrupt the done frame.
+		$reply = apply_filters( 'hyve_chat_reply_data', $data, $payload, $answered );
+
+		if ( is_array( $reply ) ) {
+			$data = $reply;
+		}
+
+		$this->send_event( 'done', $data );
+
+		exit;
+	}
+
+	/**
+	 * Send the SSE headers, disable buffering and emit an initial comment so the
+	 * client can quickly detect whether the pipe is flushable.
+	 *
+	 * @return void
+	 */
+	private function open_stream() {
+		// Streaming can outlast the default execution limit; lift it so long
+		// replies are not cut off. Discouraged on managed hosts (where it is a
+		// no-op) but required for self-hosted streaming, and guarded so a
+		// disabled function is skipped rather than silenced.
+		if ( function_exists( 'set_time_limit' ) ) {
+			set_time_limit( 0 ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_time_limit
+		}
+
+		// Stop the host from gzip-buffering the stream. ini_set fails silently
+		// when the directive is locked, so no error suppression is needed.
+		ini_set( 'zlib.output_compression', '0' ); // phpcs:ignore WordPress.PHP.IniSet.Risky
+
+		// Disable output buffering by closing any active buffers (output_buffering
+		// itself is PHP_INI_PERDIR and cannot be changed at runtime).
+		while ( ob_get_level() > 0 ) {
+			ob_end_flush();
+		}
+
+		header( 'Content-Type: text/event-stream; charset=utf-8' );
+		header( 'Cache-Control: no-cache, no-transform' );
+		header( 'Connection: keep-alive' );
+		header( 'X-Accel-Buffering: no' );
+
+		echo ': connected' . "\n\n";
+		$this->flush();
+	}
+
+	/**
+	 * Emit a named SSE event with a JSON payload.
+	 *
+	 * @param string               $event Event name.
+	 * @param array $data  Payload.
+	 *
+	 * @return void
+	 */
+	private function send_event( $event, $data ) {
+		// SSE is delivered as text/event-stream, not HTML: the event name is an
+		// internal constant and the payload is JSON-encoded, so HTML escaping
+		// would corrupt the frame and does not apply here.
+		echo 'event: ' . $event . "\n" . 'data: ' . wp_json_encode( $data ) . "\n\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+		$this->flush();
+	}
+
+	/**
+	 * Flush output to the client.
+	 *
+	 * @return void
+	 */
+	private function flush() {
+		if ( ob_get_level() > 0 ) {
+			ob_flush();
+		}
+
+		flush();
+	}
+}
diff --git a/inc/Threads.php b/inc/Threads.php
index 7d0bea17..19e04e00 100644
--- a/inc/Threads.php
+++ b/inc/Threads.php
@@ -14,6 +14,8 @@ class Threads {
 
 	public const CHART_DATA_TRANSIENT = 'hyve_charts_data';
 
+	public const MESSAGES_COUNT_TRANSIENT = 'hyve_messages_count';
+
 	/**
 	 * Constructor.
 	 */
@@ -156,9 +158,24 @@ public static function create_thread( $title, $data ) {
 		update_post_meta( $post_id, '_hyve_thread_count', 1 );
 		update_post_meta( $post_id, '_hyve_thread_id', $data['thread_id'] );
 
+		self::flush_stats_cache();
+
 		return $post_id;
 	}
 
+	/**
+	 * Invalidate the cached dashboard stats after a thread or message changes.
+	 *
+	 * Both the per-day chart and the total messages count are cached; both must
+	 * be cleared together so the Overview totals and the chart stay in sync.
+	 *
+	 * @return void
+	 */
+	private static function flush_stats_cache() {
+		delete_transient( self::CHART_DATA_TRANSIENT );
+		delete_transient( self::MESSAGES_COUNT_TRANSIENT );
+	}
+
 	/**
 	 * Add a new message to a thread.
 	 * 
@@ -189,11 +206,11 @@ public static function add_message( $post_id, $data ) {
 			[
 				'ID'                => $post_id,
 				'post_modified'     => current_time( 'mysql' ),
-				'post_modified_gmt' => current_time( 'mysql', 1 ),
+				'post_modified_gmt' => current_time( 'mysql', true ),
 			]
 		);
 
-		delete_transient( self::CHART_DATA_TRANSIENT );
+		self::flush_stats_cache();
 		return $post_id;
 	}
 
@@ -213,7 +230,7 @@ public static function get_thread_count() {
 	 * @return int
 	 */
 	public static function get_messages_count() {
-		$messages = get_transient( 'hyve_messages_count' );
+		$messages = get_transient( self::MESSAGES_COUNT_TRANSIENT );
 
 		if ( ! $messages ) {
 			global $wpdb;
@@ -224,7 +241,7 @@ public static function get_messages_count() {
 				$messages = 0;
 			}
 
-			set_transient( 'hyve_messages_count', $messages, HOUR_IN_SECONDS );
+			set_transient( self::MESSAGES_COUNT_TRANSIENT, $messages, HOUR_IN_SECONDS );
 		}
 
 		return $messages;
@@ -321,10 +338,7 @@ public static function get_chart_datasets() {
 		$messages = [];
 		$sessions = [];
 		for ( $i = $days - 1; $i >= 0; $i-- ) {
-			$timestamp = strtotime( "-{$i} days", $current_timestamp );
-			if ( false === $timestamp ) {
-				continue;
-			}
+			$timestamp  = strtotime( "-{$i} days", $current_timestamp );
 			$date_key   = gmdate( 'Y-m-d', $timestamp );
 			$labels[]   = $date_key;
 			$messages[] = isset( $messages_per_day[ $date_key ] ) ? $messages_per_day[ $date_key ] : 0;
diff --git a/migrations/20260703120000_migrate_chat_display_mode.php b/migrations/20260703120000_migrate_chat_display_mode.php
new file mode 100644
index 00000000..31093111
--- /dev/null
+++ b/migrations/20260703120000_migrate_chat_display_mode.php
@@ -0,0 +1,45 @@
+ensure_payload_index();
+
+		if ( is_wp_error( $result ) ) {
+			throw new \Exception( esc_html( $result->get_error_message() ) );
+		}
+	}
+};
diff --git a/migrations/20260711000000_encrypt_sensitive_settings.php b/migrations/20260711000000_encrypt_sensitive_settings.php
new file mode 100644
index 00000000..b28ec163
--- /dev/null
+++ b/migrations/20260711000000_encrypt_sensitive_settings.php
@@ -0,0 +1,53 @@
+=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=18.17"
       }
     },
+    "node_modules/@actions/io": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
+      "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@ampproject/remapping": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -66,14 +97,35 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@asamuzakjp/css-color": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+      "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/css-calc": "^2.1.3",
+        "@csstools/css-color-parser": "^3.0.9",
+        "@csstools/css-parser-algorithms": "^3.0.4",
+        "@csstools/css-tokenizer": "^3.0.3",
+        "lru-cache": "^10.4.3"
+      }
+    },
+    "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/@babel/code-frame": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
-      "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+      "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.29.7",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.1.1"
       },
@@ -82,9 +134,9 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz",
-      "integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+      "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -123,9 +175,9 @@
       }
     },
     "node_modules/@babel/eslint-parser": {
-      "version": "7.25.7",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.7.tgz",
-      "integrity": "sha512-B+BO9x86VYsQHimucBAL1fxTJKF4wyKY6ZVzee9QgzdZOUfs3BaR6AQrgoGrRI+7IFS1wUz/VyQ+SoBcSpdPbw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz",
+      "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -142,16 +194,16 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz",
-      "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+      "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.27.3",
-        "@babel/types": "^7.27.3",
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
         "jsesc": "^3.0.2"
       },
       "engines": {
@@ -172,14 +224,14 @@
       }
     },
     "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.27.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
-      "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+      "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/compat-data": "^7.27.2",
-        "@babel/helper-validator-option": "^7.27.1",
+        "@babel/compat-data": "^7.29.7",
+        "@babel/helper-validator-option": "^7.29.7",
         "browserslist": "^4.24.0",
         "lru-cache": "^5.1.1",
         "semver": "^6.3.1"
@@ -229,22 +281,32 @@
       }
     },
     "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.6.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz",
-      "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==",
+      "version": "0.6.8",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
+      "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-compilation-targets": "^7.22.6",
-        "@babel/helper-plugin-utils": "^7.22.5",
-        "debug": "^4.1.1",
+        "@babel/helper-compilation-targets": "^7.28.6",
+        "@babel/helper-plugin-utils": "^7.28.6",
+        "debug": "^4.4.3",
         "lodash.debounce": "^4.0.8",
-        "resolve": "^1.14.2"
+        "resolve": "^1.22.11"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+      "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/helper-member-expression-to-functions": {
       "version": "7.27.1",
       "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
@@ -260,14 +322,14 @@
       }
     },
     "node_modules/@babel/helper-module-imports": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
-      "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+      "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/traverse": "^7.27.1",
-        "@babel/types": "^7.27.1"
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -305,9 +367,9 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
-      "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+      "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -365,9 +427,9 @@
       }
     },
     "node_modules/@babel/helper-string-parser": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
-      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -375,9 +437,9 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
-      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -385,9 +447,9 @@
       }
     },
     "node_modules/@babel/helper-validator-option": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
-      "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+      "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -424,13 +486,13 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.3.tgz",
-      "integrity": "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+      "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/types": "^7.27.3"
+        "@babel/types": "^7.29.7"
       },
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -1592,17 +1654,17 @@
       }
     },
     "node_modules/@babel/plugin-transform-runtime": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.27.3.tgz",
-      "integrity": "sha512-bA9ZL5PW90YwNgGfjg6U+7Qh/k3zCEQJ06BFgAGRp/yMjw9hP9UGbGPtx3KSOkHGljEPCCxaE+PH4fUR2h1sDw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz",
+      "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-module-imports": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "babel-plugin-polyfill-corejs2": "^0.4.10",
-        "babel-plugin-polyfill-corejs3": "^0.11.0",
-        "babel-plugin-polyfill-regenerator": "^0.6.1",
+        "@babel/helper-module-imports": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "babel-plugin-polyfill-corejs2": "^0.4.14",
+        "babel-plugin-polyfill-corejs3": "^0.13.0",
+        "babel-plugin-polyfill-regenerator": "^0.6.5",
         "semver": "^6.3.1"
       },
       "engines": {
@@ -1613,14 +1675,14 @@
       }
     },
     "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
-      "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
+      "version": "0.13.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz",
+      "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.3",
-        "core-js-compat": "^3.40.0"
+        "@babel/helper-define-polyfill-provider": "^0.6.5",
+        "core-js-compat": "^3.43.0"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -1948,60 +2010,49 @@
         "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@babel/runtime": {
-      "version": "7.25.7",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
-      "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
-      "dependencies": {
-        "regenerator-runtime": "^0.14.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/template": {
-      "version": "7.27.2",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
-      "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+      "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.27.1",
-        "@babel/parser": "^7.27.2",
-        "@babel/types": "^7.27.1"
+        "@babel/code-frame": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.3.tgz",
-      "integrity": "sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+      "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/code-frame": "^7.27.1",
-        "@babel/generator": "^7.27.3",
-        "@babel/parser": "^7.27.3",
-        "@babel/template": "^7.27.2",
-        "@babel/types": "^7.27.3",
-        "debug": "^4.3.1",
-        "globals": "^11.1.0"
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "debug": "^4.3.1"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz",
-      "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==",
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+      "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-string-parser": "^7.27.1",
-        "@babel/helper-validator-identifier": "^7.27.1"
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -2014,477 +2065,759 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@colors/colors": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
-      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+    "node_modules/@cacheable/memory": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz",
+      "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==",
       "dev": true,
       "license": "MIT",
-      "optional": true,
-      "engines": {
-        "node": ">=0.1.90"
+      "dependencies": {
+        "@cacheable/utils": "^2.5.0",
+        "@keyv/bigmap": "^1.3.1",
+        "hookified": "^1.15.1",
+        "keyv": "^5.6.0"
       }
     },
-    "node_modules/@csstools/selector-specificity": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz",
-      "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==",
+    "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz",
+      "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==",
       "dev": true,
-      "license": "CC0-1.0",
-      "engines": {
-        "node": "^14 || ^16 || >=18"
+      "license": "MIT",
+      "dependencies": {
+        "hashery": "^1.4.0",
+        "hookified": "^1.15.0"
       },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/csstools"
+      "engines": {
+        "node": ">= 18"
       },
       "peerDependencies": {
-        "postcss-selector-parser": "^6.0.10"
+        "keyv": "^5.6.0"
       }
     },
-    "node_modules/@discoveryjs/json-ext": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
-      "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+    "node_modules/@cacheable/memory/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+      "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
+      "dependencies": {
+        "@keyv/serialize": "^1.1.1"
       }
     },
-    "node_modules/@es-joy/jsdoccomment": {
-      "version": "0.41.0",
-      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz",
-      "integrity": "sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==",
+    "node_modules/@cacheable/utils": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz",
+      "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "comment-parser": "1.4.1",
-        "esquery": "^1.5.0",
-        "jsdoc-type-pratt-parser": "~4.0.0"
-      },
-      "engines": {
-        "node": ">=16"
+        "hashery": "^1.5.1",
+        "keyv": "^5.6.0"
       }
     },
-    "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
-      "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+    "node_modules/@cacheable/utils/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+      "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "eslint-visitor-keys": "^3.4.3"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+        "@keyv/serialize": "^1.1.1"
       }
     },
-    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+    "node_modules/@colordx/core": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.5.0.tgz",
+      "integrity": "sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT"
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
+        "node": ">=0.1.90"
       }
     },
-    "node_modules/@eslint-community/regexpp": {
-      "version": "4.12.1",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
-      "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+    "node_modules/@csstools/color-helpers": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+      "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
       "dev": true,
-      "license": "MIT",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
       "engines": {
-        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+        "node": ">=18"
       }
     },
-    "node_modules/@eslint/eslintrc": {
+    "node_modules/@csstools/css-calc": {
       "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
-      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+      "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
       "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.12.4",
-        "debug": "^4.3.2",
-        "espree": "^9.6.0",
-        "globals": "^13.19.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.0",
-        "minimatch": "^3.1.2",
-        "strip-json-comments": "^3.1.1"
-      },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": ">=18"
       },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
-    "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/@csstools/css-color-parser": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+      "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "@csstools/color-helpers": "^5.1.0",
+        "@csstools/css-calc": "^2.1.4"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+    "node_modules/@csstools/css-parser-algorithms": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+      "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
       "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+      "peerDependencies": {
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+    "node_modules/@csstools/css-tokenizer": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+      "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
       "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
+      "engines": {
+        "node": ">=18"
       }
     },
-    "node_modules/@eslint/eslintrc/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/@csstools/media-query-list-parser": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz",
+      "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==",
       "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/type-fest": {
-      "version": "0.20.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.1",
+        "@csstools/css-tokenizer": "^3.0.1"
       }
     },
-    "node_modules/@eslint/js": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
-      "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+    "node_modules/@discoveryjs/json-ext": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+      "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": ">=10.0.0"
       }
     },
-    "node_modules/@hapi/hoek": {
-      "version": "9.3.0",
-      "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
-      "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
+    "node_modules/@dual-bundle/import-meta-resolve": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz",
+      "integrity": "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==",
       "dev": true,
-      "license": "BSD-3-Clause"
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/JounQin"
+      }
     },
-    "node_modules/@hapi/topo": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
-      "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
+    "node_modules/@emnapi/core": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+      "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
+      "optional": true,
       "dependencies": {
-        "@hapi/hoek": "^9.0.0"
+        "@emnapi/wasi-threads": "1.2.1",
+        "tslib": "^2.4.0"
       }
     },
-    "node_modules/@humanwhocodes/config-array": {
-      "version": "0.13.0",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
-      "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
-      "deprecated": "Use @eslint/config-array instead",
+    "node_modules/@emnapi/runtime": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+      "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
+      "optional": true,
       "dependencies": {
-        "@humanwhocodes/object-schema": "^2.0.3",
-        "debug": "^4.3.1",
-        "minimatch": "^3.0.5"
-      },
-      "engines": {
-        "node": ">=10.10.0"
+        "tslib": "^2.4.0"
       }
     },
-    "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/@emnapi/wasi-threads": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+      "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
       "dev": true,
       "license": "MIT",
+      "optional": true,
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "tslib": "^2.4.0"
       }
     },
-    "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/@es-joy/jsdoccomment": {
+      "version": "0.50.2",
+      "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz",
+      "integrity": "sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==",
       "dev": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "@types/estree": "^1.0.6",
+        "@typescript-eslint/types": "^8.11.0",
+        "comment-parser": "1.4.1",
+        "esquery": "^1.6.0",
+        "jsdoc-type-pratt-parser": "~4.1.0"
       },
       "engines": {
-        "node": "*"
+        "node": ">=18"
       }
     },
-    "node_modules/@humanwhocodes/module-importer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
-      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+    "node_modules/@eslint-community/eslint-plugin-eslint-comments": {
+      "version": "4.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.7.2.tgz",
+      "integrity": "sha512-LF03qURSwEWm2dz5wtdDCzNk+7Opl0X7q6I3undsaIuNsEiNvRV3BCtqu14Q/6Pzg1tBj44LcxpW2EpSLZStZw==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
+      "dependencies": {
+        "escape-string-regexp": "^4.0.0",
+        "ignore": "^7.0.5"
+      },
       "engines": {
-        "node": ">=12.22"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       },
       "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/nzakas"
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
       }
     },
-    "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
-      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
-      "deprecated": "Use @eslint/object-schema instead",
+    "node_modules/@eslint-community/eslint-plugin-eslint-comments/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
       "dev": true,
-      "license": "BSD-3-Clause"
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
     },
-    "node_modules/@inquirer/checkbox": {
-      "version": "4.1.8",
-      "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz",
-      "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==",
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+      "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@inquirer/core": "^10.1.13",
-        "@inquirer/figures": "^1.0.12",
-        "@inquirer/type": "^3.0.7",
-        "ansi-escapes": "^4.3.2",
-        "yoctocolors-cjs": "^2.1.2"
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
-        "node": ">=18"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       },
       "peerDependencies": {
-        "@types/node": ">=18"
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/@inquirer/confirm": {
-      "version": "5.1.12",
-      "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz",
-      "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==",
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
       "dev": true,
       "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/compat": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz",
+      "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==",
+      "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7"
+        "@eslint/core": "^1.2.1"
       },
       "engines": {
-        "node": ">=18"
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       },
       "peerDependencies": {
-        "@types/node": ">=18"
+        "eslint": "^8.40 || 9 || 10"
       },
       "peerDependenciesMeta": {
-        "@types/node": {
+        "eslint": {
           "optional": true
         }
       }
     },
-    "node_modules/@inquirer/core": {
-      "version": "10.1.13",
-      "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz",
-      "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==",
+    "node_modules/@eslint/config-array": {
+      "version": "0.21.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
+      "peer": true,
       "dependencies": {
-        "@inquirer/figures": "^1.0.12",
-        "@inquirer/type": "^3.0.7",
-        "ansi-escapes": "^4.3.2",
-        "cli-width": "^4.1.0",
-        "mute-stream": "^2.0.0",
-        "signal-exit": "^4.1.0",
-        "wrap-ansi": "^6.2.0",
-        "yoctocolors-cjs": "^2.1.2"
+        "@eslint/object-schema": "^2.1.7",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.5"
       },
       "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "@types/node": ">=18"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@inquirer/core/node_modules/signal-exit": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
-      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+    "node_modules/@eslint/config-helpers": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
       "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=14"
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "@eslint/core": "^0.17.0"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@inquirer/core/node_modules/wrap-ansi": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
-      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+    "node_modules/@eslint/config-helpers/node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
+      "peer": true,
       "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
+        "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": ">=8"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@inquirer/editor": {
-      "version": "4.2.13",
-      "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz",
-      "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==",
+    "node_modules/@eslint/core": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7",
-        "external-editor": "^3.1.0"
+        "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "@types/node": ">=18"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
+        "node": "^20.19.0 || ^22.13.0 || >=24"
       }
     },
-    "node_modules/@inquirer/expand": {
-      "version": "4.0.15",
-      "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz",
-      "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==",
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
+      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
-        "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7",
-        "yoctocolors-cjs": "^2.1.2"
+        "ajv": "^6.14.0",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.1",
+        "minimatch": "^3.1.5",
+        "strip-json-comments": "^3.1.1"
       },
       "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "@types/node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/@inquirer/figures": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz",
-      "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==",
+    "node_modules/@eslint/eslintrc/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0",
+      "peer": true
+    },
+    "node_modules/@eslint/eslintrc/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "engines": {
         "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@inquirer/input": {
-      "version": "4.1.12",
-      "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz",
-      "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==",
+    "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+      "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
       "license": "MIT",
+      "peer": true,
       "dependencies": {
-        "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7"
+        "argparse": "^2.0.1"
       },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
       "engines": {
-        "node": ">=18"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
-      "peerDependencies": {
-        "@types/node": ">=18"
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "@eslint/core": "^0.17.0",
+        "levn": "^0.4.1"
       },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        }
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@inquirer/number": {
-      "version": "3.0.15",
-      "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz",
-      "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==",
+    "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@formatjs/ecma402-abstract": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
+      "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@formatjs/fast-memoize": "2.2.7",
+        "@formatjs/intl-localematcher": "0.6.2",
+        "decimal.js": "^10.4.3",
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@formatjs/fast-memoize": {
+      "version": "2.2.7",
+      "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
+      "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@formatjs/icu-messageformat-parser": {
+      "version": "2.11.4",
+      "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz",
+      "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.3.6",
+        "@formatjs/icu-skeleton-parser": "1.8.16",
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@formatjs/icu-skeleton-parser": {
+      "version": "1.8.16",
+      "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz",
+      "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@formatjs/ecma402-abstract": "2.3.6",
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@formatjs/intl-localematcher": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
+      "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@hapi/address": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
+      "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@hapi/hoek": "^11.0.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@hapi/formula": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
+      "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@hapi/hoek": {
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
+      "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@hapi/pinpoint": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
+      "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@hapi/tlds": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz",
+      "integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@hapi/topo": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
+      "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@hapi/hoek": "^11.0.2"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+      "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/types": "^0.15.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.8",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+      "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.2",
+        "@humanfs/types": "^0.15.0",
+        "@humanwhocodes/retry": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/types": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+      "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@inquirer/checkbox": {
+      "version": "4.1.8",
+      "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz",
+      "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7"
+        "@inquirer/figures": "^1.0.12",
+        "@inquirer/type": "^3.0.7",
+        "ansi-escapes": "^4.3.2",
+        "yoctocolors-cjs": "^2.1.2"
       },
       "engines": {
         "node": ">=18"
@@ -2498,16 +2831,15 @@
         }
       }
     },
-    "node_modules/@inquirer/password": {
-      "version": "4.0.15",
-      "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz",
-      "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==",
+    "node_modules/@inquirer/confirm": {
+      "version": "5.1.12",
+      "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz",
+      "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@inquirer/core": "^10.1.13",
-        "@inquirer/type": "^3.0.7",
-        "ansi-escapes": "^4.3.2"
+        "@inquirer/type": "^3.0.7"
       },
       "engines": {
         "node": ">=18"
@@ -2521,23 +2853,21 @@
         }
       }
     },
-    "node_modules/@inquirer/prompts": {
-      "version": "7.5.3",
-      "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.5.3.tgz",
-      "integrity": "sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==",
+    "node_modules/@inquirer/core": {
+      "version": "10.1.13",
+      "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz",
+      "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@inquirer/checkbox": "^4.1.8",
-        "@inquirer/confirm": "^5.1.12",
-        "@inquirer/editor": "^4.2.13",
-        "@inquirer/expand": "^4.0.15",
-        "@inquirer/input": "^4.1.12",
-        "@inquirer/number": "^3.0.15",
-        "@inquirer/password": "^4.0.15",
-        "@inquirer/rawlist": "^4.1.3",
-        "@inquirer/search": "^3.0.15",
-        "@inquirer/select": "^4.2.3"
+        "@inquirer/figures": "^1.0.12",
+        "@inquirer/type": "^3.0.7",
+        "ansi-escapes": "^4.3.2",
+        "cli-width": "^4.1.0",
+        "mute-stream": "^2.0.0",
+        "signal-exit": "^4.1.0",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.2"
       },
       "engines": {
         "node": ">=18"
@@ -2551,16 +2881,44 @@
         }
       }
     },
-    "node_modules/@inquirer/rawlist": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz",
-      "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==",
+    "node_modules/@inquirer/core/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@inquirer/core/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@inquirer/editor": {
+      "version": "4.2.13",
+      "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz",
+      "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@inquirer/core": "^10.1.13",
         "@inquirer/type": "^3.0.7",
-        "yoctocolors-cjs": "^2.1.2"
+        "external-editor": "^3.1.0"
       },
       "engines": {
         "node": ">=18"
@@ -2574,15 +2932,168 @@
         }
       }
     },
-    "node_modules/@inquirer/search": {
-      "version": "3.0.15",
-      "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz",
-      "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==",
+    "node_modules/@inquirer/expand": {
+      "version": "4.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz",
+      "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@inquirer/core": "^10.1.13",
-        "@inquirer/figures": "^1.0.12",
+        "@inquirer/type": "^3.0.7",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/figures": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz",
+      "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/input": {
+      "version": "4.1.12",
+      "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz",
+      "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.1.13",
+        "@inquirer/type": "^3.0.7"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/number": {
+      "version": "3.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz",
+      "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.1.13",
+        "@inquirer/type": "^3.0.7"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/password": {
+      "version": "4.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz",
+      "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.1.13",
+        "@inquirer/type": "^3.0.7",
+        "ansi-escapes": "^4.3.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts": {
+      "version": "7.5.3",
+      "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.5.3.tgz",
+      "integrity": "sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/checkbox": "^4.1.8",
+        "@inquirer/confirm": "^5.1.12",
+        "@inquirer/editor": "^4.2.13",
+        "@inquirer/expand": "^4.0.15",
+        "@inquirer/input": "^4.1.12",
+        "@inquirer/number": "^3.0.15",
+        "@inquirer/password": "^4.0.15",
+        "@inquirer/rawlist": "^4.1.3",
+        "@inquirer/search": "^3.0.15",
+        "@inquirer/select": "^4.2.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/rawlist": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz",
+      "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.1.13",
+        "@inquirer/type": "^3.0.7",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/search": {
+      "version": "3.0.15",
+      "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz",
+      "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.1.13",
+        "@inquirer/figures": "^1.0.12",
         "@inquirer/type": "^3.0.7",
         "yoctocolors-cjs": "^2.1.2"
       },
@@ -2765,9 +3276,9 @@
       }
     },
     "node_modules/@istanbuljs/schema": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
+      "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -2856,132 +3367,380 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "node_modules/@jest/expect": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
-      "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+    "node_modules/@jest/environment-jsdom-abstract": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.4.1.tgz",
+      "integrity": "sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "expect": "^29.7.0",
-        "jest-snapshot": "^29.7.0"
+        "@jest/environment": "30.4.1",
+        "@jest/fake-timers": "30.4.1",
+        "@jest/types": "30.4.1",
+        "@types/jsdom": "^21.1.7",
+        "@types/node": "*",
+        "jest-mock": "30.4.1",
+        "jest-util": "30.4.1"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "canvas": "^3.0.0",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@jest/expect-utils": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
-      "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/environment": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz",
+      "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "jest-get-type": "^29.6.3"
+        "@jest/fake-timers": "30.4.1",
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "jest-mock": "30.4.1"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       }
     },
-    "node_modules/@jest/fake-timers": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
-      "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/fake-timers": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz",
+      "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@sinonjs/fake-timers": "^10.0.2",
+        "@jest/types": "30.4.1",
+        "@sinonjs/fake-timers": "^15.4.0",
         "@types/node": "*",
-        "jest-message-util": "^29.7.0",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0"
+        "jest-message-util": "30.4.1",
+        "jest-mock": "30.4.1",
+        "jest-util": "30.4.1"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       }
     },
-    "node_modules/@jest/globals": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
-      "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/schemas": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
+      "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/expect": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "jest-mock": "^29.7.0"
+        "@sinclair/typebox": "^0.34.0"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       }
     },
-    "node_modules/@jest/reporters": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
-      "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/types": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz",
+      "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@bcoe/v8-coverage": "^0.2.3",
-        "@jest/console": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@jridgewell/trace-mapping": "^0.3.18",
+        "@jest/pattern": "30.4.0",
+        "@jest/schemas": "30.4.1",
+        "@types/istanbul-lib-coverage": "^2.0.6",
+        "@types/istanbul-reports": "^3.0.4",
         "@types/node": "*",
-        "chalk": "^4.0.0",
-        "collect-v8-coverage": "^1.0.0",
-        "exit": "^0.1.2",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-instrument": "^6.0.0",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.0",
-        "istanbul-reports": "^3.1.3",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "slash": "^3.0.0",
-        "string-length": "^4.0.1",
-        "strip-ansi": "^6.0.0",
-        "v8-to-istanbul": "^9.0.1"
+        "@types/yargs": "^17.0.33",
+        "chalk": "^4.1.2"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       }
     },
-    "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
-      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@sinclair/typebox": {
+      "version": "0.34.49",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
+      "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/@sinonjs/fake-timers": {
+      "version": "15.4.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz",
+      "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "@babel/core": "^7.23.9",
-        "@babel/parser": "^7.23.9",
-        "@istanbuljs/schema": "^0.1.3",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^7.5.4"
-      },
+        "@sinonjs/commons": "^3.0.1"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/ci-info": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+      "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/jest-message-util": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz",
+      "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@jest/types": "30.4.1",
+        "@types/stack-utils": "^2.0.3",
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "jest-util": "30.4.1",
+        "picomatch": "^4.0.3",
+        "pretty-format": "30.4.1",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.6"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/jest-mock": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz",
+      "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "jest-util": "30.4.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/jest-util": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz",
+      "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "ci-info": "^4.2.0",
+        "graceful-fs": "^4.2.11",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/picomatch": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@jest/environment-jsdom-abstract/node_modules/pretty-format": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz",
+      "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/schemas": "30.4.1",
+        "ansi-styles": "^5.2.0",
+        "react-is-18": "npm:react-is@^18.3.1",
+        "react-is-19": "npm:react-is@^19.2.5"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/expect": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+      "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "expect": "^29.7.0",
+        "jest-snapshot": "^29.7.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/expect-utils": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+      "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "jest-get-type": "^29.6.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/fake-timers": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+      "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "^29.6.3",
+        "@sinonjs/fake-timers": "^10.0.2",
+        "@types/node": "*",
+        "jest-message-util": "^29.7.0",
+        "jest-mock": "^29.7.0",
+        "jest-util": "^29.7.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/globals": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+      "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/environment": "^29.7.0",
+        "@jest/expect": "^29.7.0",
+        "@jest/types": "^29.6.3",
+        "jest-mock": "^29.7.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/pattern": {
+      "version": "30.4.0",
+      "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz",
+      "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "jest-regex-util": "30.4.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/pattern/node_modules/jest-regex-util": {
+      "version": "30.4.0",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz",
+      "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/reporters": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+      "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
+        "@jridgewell/trace-mapping": "^0.3.18",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "exit": "^0.1.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^6.0.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.1.3",
+        "jest-message-util": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-worker": "^29.7.0",
+        "slash": "^3.0.0",
+        "string-length": "^4.0.1",
+        "strip-ansi": "^6.0.0",
+        "v8-to-istanbul": "^9.0.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/core": "^7.23.9",
+        "@babel/parser": "^7.23.9",
+        "@istanbuljs/schema": "^0.1.3",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^7.5.4"
+      },
       "engines": {
         "node": ">=10"
       }
     },
     "node_modules/@jest/reporters/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -3097,28 +3856,13 @@
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
-      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/set-array": "^1.2.1",
-        "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/remapping": {
-      "version": "2.3.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
-      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/sourcemap-codec": "^1.5.0",
         "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
@@ -3132,16 +3876,6 @@
         "node": ">=6.0.0"
       }
     },
-    "node_modules/@jridgewell/set-array": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
-      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
     "node_modules/@jridgewell/source-map": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
@@ -3161,9 +3895,9 @@
       "license": "MIT"
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.25",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
-      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -3171,6 +3905,13 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
+    "node_modules/@keyv/serialize": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz",
+      "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@kurkle/color": {
       "version": "0.3.4",
       "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
@@ -3201,6 +3942,25 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@napi-rs/wasm-runtime": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+      "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@tybys/wasm-util": "^0.10.3"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      },
+      "peerDependencies": {
+        "@emnapi/core": "^1.7.1",
+        "@emnapi/runtime": "^1.7.1"
+      }
+    },
     "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
       "version": "5.1.1-v1",
       "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -3211,6 +3971,19 @@
         "eslint-scope": "5.1.1"
       }
     },
+    "node_modules/@nodable/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodable"
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -3249,1524 +4022,3884 @@
         "node": ">= 8"
       }
     },
-    "node_modules/@octokit/auth-token": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz",
-      "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==",
+    "node_modules/@octokit/app": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz",
+      "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "@octokit/auth-app": "^6.0.0",
+        "@octokit/auth-unauthenticated": "^5.0.0",
+        "@octokit/core": "^5.0.0",
+        "@octokit/oauth-app": "^6.0.0",
+        "@octokit/plugin-paginate-rest": "^9.0.0",
+        "@octokit/types": "^12.0.0",
+        "@octokit/webhooks": "^12.0.4"
+      },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/core": {
-      "version": "4.2.4",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz",
-      "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==",
+    "node_modules/@octokit/app/node_modules/@octokit/auth-token": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+      "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@octokit/app/node_modules/@octokit/core": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
+      "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/auth-token": "^3.0.0",
-        "@octokit/graphql": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
+        "@octokit/auth-token": "^4.0.0",
+        "@octokit/graphql": "^7.1.0",
+        "@octokit/request": "^8.4.1",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.0.0",
         "before-after-hook": "^2.2.0",
         "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/endpoint": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz",
-      "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==",
+    "node_modules/@octokit/app/node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/app/node_modules/@octokit/core/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
+        "@octokit/openapi-types": "^24.2.0"
+      }
+    },
+    "node_modules/@octokit/app/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/types": "^13.1.0",
         "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/endpoint/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==",
+    "node_modules/@octokit/app/node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/app/node_modules/@octokit/endpoint/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@octokit/graphql": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz",
-      "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==",
+    "node_modules/@octokit/app/node_modules/@octokit/graphql": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
+      "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
+        "@octokit/request": "^8.4.1",
+        "@octokit/types": "^13.0.0",
         "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/openapi-types": {
-      "version": "18.1.1",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz",
-      "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==",
+    "node_modules/@octokit/app/node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@octokit/plugin-paginate-rest": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz",
-      "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==",
+    "node_modules/@octokit/app/node_modules/@octokit/graphql/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/tsconfig": "^1.0.2",
-        "@octokit/types": "^9.2.3"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=4"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@octokit/plugin-retry": {
-      "version": "4.1.6",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.1.6.tgz",
-      "integrity": "sha512-obkYzIgEC75r8+9Pnfiiqy3y/x1bc3QLE5B7qvv9wi9Kj0R5tGQFC6QMBg1154WQ9lAVypuQDGyp3hNpp15gQQ==",
+    "node_modules/@octokit/app/node_modules/@octokit/openapi-types": {
+      "version": "20.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
+      "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
       "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "bottleneck": "^2.15.3"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
+      "license": "MIT"
     },
-    "node_modules/@octokit/plugin-throttling": {
-      "version": "5.2.3",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-5.2.3.tgz",
-      "integrity": "sha512-C9CFg9mrf6cugneKiaI841iG8DOv6P5XXkjmiNNut+swePxQ7RWEdAZRp5rJoE1hjsIqiYcKa/ZkOQ+ujPI39Q==",
+    "node_modules/@octokit/app/node_modules/@octokit/plugin-paginate-rest": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
+      "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "bottleneck": "^2.15.3"
+        "@octokit/types": "^12.6.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       },
       "peerDependencies": {
-        "@octokit/core": "^4.0.0"
+        "@octokit/core": "5"
       }
     },
-    "node_modules/@octokit/request": {
-      "version": "6.2.8",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz",
-      "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==",
+    "node_modules/@octokit/app/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/endpoint": "^7.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
         "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/request-error": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
-      "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
+    "node_modules/@octokit/app/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/types": "^9.0.0",
+        "@octokit/types": "^13.1.0",
         "deprecation": "^2.0.0",
         "once": "^1.4.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@octokit/request/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==",
+    "node_modules/@octokit/app/node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/app/node_modules/@octokit/request-error/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@octokit/tsconfig": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz",
-      "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==",
+    "node_modules/@octokit/app/node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@octokit/types": {
-      "version": "9.3.2",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz",
-      "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==",
+    "node_modules/@octokit/app/node_modules/@octokit/request/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/openapi-types": "^18.0.0"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@pkgjs/parseargs": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
-      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+    "node_modules/@octokit/app/node_modules/@octokit/types": {
+      "version": "12.6.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+      "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
       "dev": true,
       "license": "MIT",
-      "optional": true,
-      "engines": {
-        "node": ">=14"
+      "dependencies": {
+        "@octokit/openapi-types": "^20.0.0"
       }
     },
-    "node_modules/@pkgr/core": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
-      "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
+    "node_modules/@octokit/app/node_modules/before-after-hook": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/@octokit/app/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@octokit/auth-app": {
+      "version": "6.1.4",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz",
+      "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      "dependencies": {
+        "@octokit/auth-oauth-app": "^7.1.0",
+        "@octokit/auth-oauth-user": "^4.1.0",
+        "@octokit/request": "^8.3.1",
+        "@octokit/request-error": "^5.1.0",
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.3.1",
+        "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1",
+        "universal-github-app-jwt": "^1.1.2",
+        "universal-user-agent": "^6.0.0"
       },
-      "funding": {
-        "url": "https://opencollective.com/pkgr"
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@playwright/test": {
-      "version": "1.53.2",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
-      "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
+    "node_modules/@octokit/auth-app/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "playwright": "1.53.2"
-      },
-      "bin": {
-        "playwright": "cli.js"
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">= 18"
       }
     },
-    "node_modules/@pmmmwh/react-refresh-webpack-plugin": {
-      "version": "0.5.15",
-      "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz",
-      "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==",
+    "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/auth-app/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ansi-html": "^0.0.9",
-        "core-js-pure": "^3.23.3",
-        "error-stack-parser": "^2.0.6",
-        "html-entities": "^2.1.0",
-        "loader-utils": "^2.0.4",
-        "schema-utils": "^4.2.0",
-        "source-map": "^0.7.3"
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 10.13"
-      },
-      "peerDependencies": {
-        "@types/webpack": "4.x || 5.x",
-        "react-refresh": ">=0.10.0 <1.0.0",
-        "sockjs-client": "^1.4.0",
-        "type-fest": ">=0.17.0 <5.0.0",
-        "webpack": ">=4.43.0 <6.0.0",
-        "webpack-dev-server": "3.x || 4.x || 5.x",
-        "webpack-hot-middleware": "2.x",
-        "webpack-plugin-serve": "0.x || 1.x"
-      },
-      "peerDependenciesMeta": {
-        "@types/webpack": {
-          "optional": true
-        },
-        "sockjs-client": {
-          "optional": true
-        },
-        "type-fest": {
-          "optional": true
-        },
-        "webpack-dev-server": {
-          "optional": true
-        },
-        "webpack-hot-middleware": {
-          "optional": true
-        },
-        "webpack-plugin-serve": {
-          "optional": true
-        }
+        "node": ">= 18"
       }
     },
-    "node_modules/@pnpm/config.env-replace": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
-      "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+    "node_modules/@octokit/auth-app/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
+      },
       "engines": {
-        "node": ">=12.22.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/@pnpm/network.ca-file": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
-      "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+    "node_modules/@octokit/auth-app/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "graceful-fs": "4.2.10"
-      },
+        "@octokit/openapi-types": "^24.2.0"
+      }
+    },
+    "node_modules/@octokit/auth-app/node_modules/lru-cache": {
+      "name": "@wolfy1339/lru-cache",
+      "version": "11.0.2-patch.1",
+      "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz",
+      "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==",
+      "dev": true,
+      "license": "ISC",
       "engines": {
-        "node": ">=12.22.0"
+        "node": "18 >=18.20 || 20 || >=22"
       }
     },
-    "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
-      "version": "4.2.10",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
-      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+    "node_modules/@octokit/auth-app/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
       "dev": true,
       "license": "ISC"
     },
-    "node_modules/@pnpm/npm-conf": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
-      "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
+    "node_modules/@octokit/auth-oauth-app": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz",
+      "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@pnpm/config.env-replace": "^1.1.0",
-        "@pnpm/network.ca-file": "^1.0.1",
-        "config-chain": "^1.1.11"
+        "@octokit/auth-oauth-device": "^6.1.0",
+        "@octokit/auth-oauth-user": "^4.1.0",
+        "@octokit/request": "^8.3.1",
+        "@octokit/types": "^13.0.0",
+        "@types/btoa-lite": "^1.0.0",
+        "btoa-lite": "^1.0.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">= 18"
       }
     },
-    "node_modules/@polka/url": {
-      "version": "1.0.0-next.25",
-      "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
-      "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
+    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@puppeteer/browsers": {
-      "version": "1.4.6",
-      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz",
-      "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==",
+    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
       "dependencies": {
-        "debug": "4.3.4",
-        "extract-zip": "2.0.1",
-        "progress": "2.0.3",
-        "proxy-agent": "6.3.0",
-        "tar-fs": "3.0.4",
-        "unbzip2-stream": "1.4.3",
-        "yargs": "17.7.1"
-      },
-      "bin": {
-        "browsers": "lib/cjs/main-cli.js"
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=16.3.0"
-      },
-      "peerDependencies": {
-        "typescript": ">= 4.7.4"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "node": ">= 18"
       }
     },
-    "node_modules/@puppeteer/browsers/node_modules/debug": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ms": "2.1.2"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
+        "node": ">= 18"
       }
     },
-    "node_modules/@puppeteer/browsers/node_modules/tar-fs": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
-      "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
+    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "mkdirp-classic": "^0.5.2",
-        "pump": "^3.0.0",
-        "tar-stream": "^3.1.5"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@puppeteer/browsers/node_modules/tar-stream": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
-      "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+    "node_modules/@octokit/auth-oauth-app/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@octokit/auth-oauth-device": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz",
+      "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "b4a": "^1.6.4",
-        "fast-fifo": "^1.2.0",
-        "streamx": "^2.15.0"
+        "@octokit/oauth-methods": "^4.1.0",
+        "@octokit/request": "^8.3.1",
+        "@octokit/types": "^13.0.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@puppeteer/browsers/node_modules/yargs": {
-      "version": "17.7.1",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
-      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cliui": "^8.0.1",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.3",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^21.1.1"
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">= 18"
       }
     },
-    "node_modules/@rtsao/scc": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
-      "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@semantic-release/changelog": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz",
-      "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@semantic-release/error": "^3.0.0",
-        "aggregate-error": "^3.0.0",
-        "fs-extra": "^11.0.0",
-        "lodash": "^4.17.4"
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/commit-analyzer": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz",
-      "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "conventional-changelog-angular": "^5.0.0",
-        "conventional-commits-filter": "^2.0.0",
-        "conventional-commits-parser": "^3.2.3",
-        "debug": "^4.0.0",
-        "import-from": "^4.0.0",
-        "lodash": "^4.17.4",
-        "micromatch": "^4.0.2"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0-beta.1"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/error": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz",
-      "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14.17"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@semantic-release/exec": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz",
-      "integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==",
+    "node_modules/@octokit/auth-oauth-device/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@octokit/auth-oauth-user": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz",
+      "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@semantic-release/error": "^3.0.0",
-        "aggregate-error": "^3.0.0",
-        "debug": "^4.0.0",
-        "execa": "^5.0.0",
-        "lodash": "^4.17.4",
-        "parse-json": "^5.0.0"
+        "@octokit/auth-oauth-device": "^6.1.0",
+        "@octokit/oauth-methods": "^4.1.0",
+        "@octokit/request": "^8.3.1",
+        "@octokit/types": "^13.0.0",
+        "btoa-lite": "^1.0.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/git": {
-      "version": "10.0.1",
-      "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz",
-      "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==",
+    "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@semantic-release/error": "^3.0.0",
-        "aggregate-error": "^3.0.0",
-        "debug": "^4.0.0",
-        "dir-glob": "^3.0.0",
-        "execa": "^5.0.0",
-        "lodash": "^4.17.4",
-        "micromatch": "^4.0.0",
-        "p-reduce": "^2.0.0"
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/github": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.1.0.tgz",
-      "integrity": "sha512-erR9E5rpdsz0dW1I7785JtndQuMWN/iDcemcptf67tBNOmBUN0b2YNOgcjYUnBpgRpZ5ozfBHrK7Bz+2ets/Dg==",
+    "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@octokit/core": "^4.2.1",
-        "@octokit/plugin-paginate-rest": "^6.1.2",
-        "@octokit/plugin-retry": "^4.1.3",
-        "@octokit/plugin-throttling": "^5.2.3",
-        "@semantic-release/error": "^3.0.0",
-        "aggregate-error": "^3.0.0",
-        "debug": "^4.0.0",
-        "dir-glob": "^3.0.0",
-        "fs-extra": "^11.0.0",
-        "globby": "^11.0.0",
-        "http-proxy-agent": "^7.0.0",
-        "https-proxy-agent": "^7.0.0",
-        "issue-parser": "^6.0.0",
-        "lodash": "^4.17.4",
-        "mime": "^3.0.0",
-        "p-filter": "^2.0.0",
-        "url-join": "^4.0.0"
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0-beta.1"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/github/node_modules/agent-base": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
-      "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+    "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "debug": "^4.3.4"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/github/node_modules/http-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+    "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "agent-base": "^7.1.0",
-        "debug": "^4.3.4"
-      },
+        "@octokit/openapi-types": "^24.2.0"
+      }
+    },
+    "node_modules/@octokit/auth-oauth-user/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@octokit/auth-token": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
+      "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
+      "dev": true,
+      "license": "MIT",
       "engines": {
-        "node": ">= 14"
+        "node": ">= 20"
       }
     },
-    "node_modules/@semantic-release/github/node_modules/https-proxy-agent": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
-      "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+    "node_modules/@octokit/auth-unauthenticated": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz",
+      "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "agent-base": "^7.0.2",
-        "debug": "4"
+        "@octokit/request-error": "^5.0.0",
+        "@octokit/types": "^12.0.0"
       },
       "engines": {
-        "node": ">= 14"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/npm": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.2.tgz",
-      "integrity": "sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g==",
+    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": {
+      "version": "20.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
+      "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@semantic-release/error": "^3.0.0",
-        "aggregate-error": "^3.0.0",
-        "execa": "^5.0.0",
-        "fs-extra": "^11.0.0",
-        "lodash": "^4.17.15",
-        "nerf-dart": "^1.0.0",
-        "normalize-url": "^6.0.0",
-        "npm": "^8.3.0",
-        "rc": "^1.2.8",
-        "read-pkg": "^5.0.0",
-        "registry-auth-token": "^5.0.0",
-        "semver": "^7.1.2",
-        "tempy": "^1.0.0"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">=16 || ^14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=19.0.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/@semantic-release/npm/node_modules/hosted-git-info": {
-      "version": "2.8.9",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
-      "license": "ISC"
+      "license": "MIT"
     },
-    "node_modules/@semantic-release/npm/node_modules/normalize-package-data": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/request-error/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
-      "license": "BSD-2-Clause",
+      "license": "MIT",
       "dependencies": {
-        "hosted-git-info": "^2.1.4",
-        "resolve": "^1.10.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@semantic-release/npm/node_modules/normalize-package-data/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": {
+      "version": "12.6.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+      "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver"
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/openapi-types": "^20.0.0"
       }
     },
-    "node_modules/@semantic-release/npm/node_modules/read-pkg": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
-      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+    "node_modules/@octokit/core": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
+      "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@types/normalize-package-data": "^2.4.0",
-        "normalize-package-data": "^2.5.0",
-        "parse-json": "^5.0.0",
-        "type-fest": "^0.6.0"
+        "@octokit/auth-token": "^6.0.0",
+        "@octokit/graphql": "^9.0.3",
+        "@octokit/request": "^10.0.6",
+        "@octokit/request-error": "^7.0.2",
+        "@octokit/types": "^16.0.0",
+        "before-after-hook": "^4.0.0",
+        "universal-user-agent": "^7.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">= 20"
       }
     },
-    "node_modules/@semantic-release/npm/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+    "node_modules/@octokit/endpoint": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz",
+      "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/types": "^16.0.0",
+        "universal-user-agent": "^7.0.2"
       },
       "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@semantic-release/npm/node_modules/type-fest": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
-      "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=8"
+        "node": ">= 20"
       }
     },
-    "node_modules/@semantic-release/release-notes-generator": {
-      "version": "10.0.3",
-      "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz",
-      "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==",
+    "node_modules/@octokit/graphql": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
+      "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "conventional-changelog-angular": "^5.0.0",
-        "conventional-changelog-writer": "^5.0.0",
-        "conventional-commits-filter": "^2.0.0",
-        "conventional-commits-parser": "^3.2.3",
-        "debug": "^4.0.0",
-        "get-stream": "^6.0.0",
-        "import-from": "^4.0.0",
-        "into-stream": "^6.0.0",
-        "lodash": "^4.17.4",
-        "read-pkg-up": "^7.0.0"
+        "@octokit/request": "^10.0.6",
+        "@octokit/types": "^16.0.0",
+        "universal-user-agent": "^7.0.0"
       },
       "engines": {
-        "node": ">=14.17"
-      },
-      "peerDependencies": {
-        "semantic-release": ">=18.0.0-beta.1"
+        "node": ">= 20"
       }
     },
-    "node_modules/@sentry/core": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz",
-      "integrity": "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==",
+    "node_modules/@octokit/oauth-app": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz",
+      "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sentry/hub": "6.19.7",
-        "@sentry/minimal": "6.19.7",
-        "@sentry/types": "6.19.7",
-        "@sentry/utils": "6.19.7",
-        "tslib": "^1.9.3"
+        "@octokit/auth-oauth-app": "^7.0.0",
+        "@octokit/auth-oauth-user": "^4.0.0",
+        "@octokit/auth-unauthenticated": "^5.0.0",
+        "@octokit/core": "^5.0.0",
+        "@octokit/oauth-authorization-url": "^6.0.2",
+        "@octokit/oauth-methods": "^4.0.0",
+        "@types/aws-lambda": "^8.10.83",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/core/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/auth-token": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+      "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
       "dev": true,
-      "license": "0BSD"
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      }
     },
-    "node_modules/@sentry/hub": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz",
-      "integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/core": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
+      "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sentry/types": "6.19.7",
-        "@sentry/utils": "6.19.7",
-        "tslib": "^1.9.3"
+        "@octokit/auth-token": "^4.0.0",
+        "@octokit/graphql": "^7.1.0",
+        "@octokit/request": "^8.4.1",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.0.0",
+        "before-after-hook": "^2.2.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/hub/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-      "dev": true,
-      "license": "0BSD"
-    },
-    "node_modules/@sentry/minimal": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz",
-      "integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sentry/hub": "6.19.7",
-        "@sentry/types": "6.19.7",
-        "tslib": "^1.9.3"
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/minimal/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-      "dev": true,
-      "license": "0BSD"
-    },
-    "node_modules/@sentry/node": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.19.7.tgz",
-      "integrity": "sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/graphql": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
+      "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sentry/core": "6.19.7",
-        "@sentry/hub": "6.19.7",
-        "@sentry/types": "6.19.7",
-        "@sentry/utils": "6.19.7",
-        "cookie": "^0.4.1",
-        "https-proxy-agent": "^5.0.0",
-        "lru_map": "^0.3.3",
-        "tslib": "^1.9.3"
+        "@octokit/request": "^8.4.1",
+        "@octokit/types": "^13.0.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/node/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
-      "license": "0BSD"
+      "license": "MIT"
     },
-    "node_modules/@sentry/types": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz",
-      "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
+      },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/utils": {
-      "version": "6.19.7",
-      "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz",
-      "integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sentry/types": "6.19.7",
-        "tslib": "^1.9.3"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sentry/utils/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-      "dev": true,
-      "license": "0BSD"
-    },
-    "node_modules/@sideway/address": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
-      "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
+    "node_modules/@octokit/oauth-app/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@hapi/hoek": "^9.0.0"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@sideway/formula": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
-      "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@sideway/pinpoint": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
-      "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
+    "node_modules/@octokit/oauth-app/node_modules/before-after-hook": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
       "dev": true,
-      "license": "BSD-3-Clause"
+      "license": "Apache-2.0"
     },
-    "node_modules/@sinclair/typebox": {
-      "version": "0.27.8",
-      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
-      "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+    "node_modules/@octokit/oauth-app/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
       "dev": true,
-      "license": "MIT"
+      "license": "ISC"
     },
-    "node_modules/@sindresorhus/is": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
-      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+    "node_modules/@octokit/oauth-authorization-url": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
+      "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/is?sponsor=1"
+        "node": ">= 18"
       }
     },
-    "node_modules/@sinonjs/commons": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
-      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+    "node_modules/@octokit/oauth-methods": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz",
+      "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "type-detect": "4.0.8"
+        "@octokit/oauth-authorization-url": "^6.0.2",
+        "@octokit/request": "^8.3.1",
+        "@octokit/request-error": "^5.1.0",
+        "@octokit/types": "^13.0.0",
+        "btoa-lite": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@sinonjs/fake-timers": {
-      "version": "10.3.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
-      "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+    "node_modules/@octokit/oauth-methods/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
-      "license": "BSD-3-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@sinonjs/commons": "^3.0.0"
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
-      "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
+    "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
+      "license": "MIT"
     },
-    "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
-      "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+    "node_modules/@octokit/oauth-methods/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "dependencies": {
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
-      "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+    "node_modules/@octokit/oauth-methods/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "dependencies": {
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
-      "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+    "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
-      "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
+    "node_modules/@octokit/oauth-methods/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@octokit/openapi-types": {
+      "version": "27.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
+      "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/plugin-paginate-graphql": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz",
+      "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+        "node": ">= 18"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+        "@octokit/core": ">=5"
       }
     },
-    "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
-      "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
+    "node_modules/@octokit/plugin-paginate-rest": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
+      "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14"
+      "dependencies": {
+        "@octokit/types": "^16.0.0"
       },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "engines": {
+        "node": ">= 20"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+        "@octokit/core": ">=6"
       }
     },
-    "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+    "node_modules/@octokit/plugin-retry": {
       "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
-      "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.1.0.tgz",
+      "integrity": "sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=14"
+      "dependencies": {
+        "@octokit/request-error": "^7.0.2",
+        "@octokit/types": "^16.0.0",
+        "bottleneck": "^2.15.3"
       },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "engines": {
+        "node": ">= 20"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+        "@octokit/core": ">=7"
       }
     },
-    "node_modules/@svgr/babel-plugin-transform-svg-component": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
-      "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+    "node_modules/@octokit/plugin-throttling": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz",
+      "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=12"
+      "dependencies": {
+        "@octokit/types": "^16.0.0",
+        "bottleneck": "^2.15.3"
       },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "engines": {
+        "node": ">= 20"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+        "@octokit/core": "^7.0.0"
       }
     },
-    "node_modules/@svgr/babel-preset": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
-      "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
+    "node_modules/@octokit/request": {
+      "version": "10.0.10",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.10.tgz",
+      "integrity": "sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
-        "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
-        "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
-        "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
-        "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
-        "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
-        "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
-        "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+        "@octokit/endpoint": "^11.0.3",
+        "@octokit/request-error": "^7.0.2",
+        "@octokit/types": "^16.0.0",
+        "content-type": "^2.0.0",
+        "json-with-bigint": "^3.5.3",
+        "universal-user-agent": "^7.0.2"
       },
       "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
+        "node": ">= 20"
       }
     },
-    "node_modules/@svgr/core": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
-      "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+    "node_modules/@octokit/request-error": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
+      "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/core": "^7.21.3",
-        "@svgr/babel-preset": "8.1.0",
-        "camelcase": "^6.2.0",
-        "cosmiconfig": "^8.1.3",
-        "snake-case": "^3.0.4"
+        "@octokit/types": "^16.0.0"
       },
       "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+        "node": ">= 20"
       }
     },
-    "node_modules/@svgr/hast-util-to-babel-ast": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
-      "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
+    "node_modules/@octokit/request/node_modules/content-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+      "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.21.3",
-        "entities": "^4.4.0"
-      },
       "engines": {
-        "node": ">=14"
+        "node": ">=18"
       },
       "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
       }
     },
-    "node_modules/@svgr/plugin-jsx": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
-      "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
+    "node_modules/@octokit/types": {
+      "version": "16.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
+      "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/core": "^7.21.3",
-        "@svgr/babel-preset": "8.1.0",
-        "@svgr/hast-util-to-babel-ast": "8.0.0",
-        "svg-parser": "^2.0.4"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
-      },
-      "peerDependencies": {
-        "@svgr/core": "*"
+        "@octokit/openapi-types": "^27.0.0"
       }
     },
-    "node_modules/@svgr/plugin-svgo": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz",
-      "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==",
+    "node_modules/@octokit/webhooks": {
+      "version": "12.3.2",
+      "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.2.tgz",
+      "integrity": "sha512-exj1MzVXoP7xnAcAB3jZ97pTvVPkQF9y6GA/dvYC47HV7vLv+24XRS6b/v/XnyikpEuvMhugEXdGtAlU086WkQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cosmiconfig": "^8.1.3",
-        "deepmerge": "^4.3.1",
-        "svgo": "^3.0.2"
+        "@octokit/request-error": "^5.0.0",
+        "@octokit/webhooks-methods": "^4.1.0",
+        "@octokit/webhooks-types": "7.6.1",
+        "aggregate-error": "^3.1.0"
       },
       "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
-      },
-      "peerDependencies": {
-        "@svgr/core": "*"
+        "node": ">= 18"
       }
     },
-    "node_modules/@svgr/webpack": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz",
-      "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==",
+    "node_modules/@octokit/webhooks-methods": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz",
+      "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@octokit/webhooks-types": {
+      "version": "7.6.1",
+      "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz",
+      "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/webhooks/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@octokit/webhooks/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/core": "^7.21.3",
-        "@babel/plugin-transform-react-constant-elements": "^7.21.3",
-        "@babel/preset-env": "^7.20.2",
-        "@babel/preset-react": "^7.18.6",
-        "@babel/preset-typescript": "^7.21.0",
-        "@svgr/core": "8.1.0",
-        "@svgr/plugin-jsx": "8.1.0",
-        "@svgr/plugin-svgo": "8.1.0"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/@octokit/webhooks/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
+      }
+    },
+    "node_modules/@opentelemetry/api": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+      "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@opentelemetry/api-logs": {
+      "version": "0.57.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz",
+      "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0"
       },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/context-async-hooks": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz",
+      "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==",
+      "dev": true,
+      "license": "Apache-2.0",
       "engines": {
         "node": ">=14"
       },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/gregberge"
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
       }
     },
-    "node_modules/@szmarczak/http-timer": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
-      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+    "node_modules/@opentelemetry/core": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz",
+      "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "defer-to-connect": "^2.0.0"
+        "@opentelemetry/semantic-conventions": "1.28.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
       }
     },
-    "node_modules/@tailwindcss/node": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
-      "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==",
+    "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation": {
+      "version": "0.57.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
+      "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
+      "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "@jridgewell/remapping": "^2.3.4",
-        "enhanced-resolve": "^5.18.3",
-        "jiti": "^2.6.1",
-        "lightningcss": "1.30.2",
-        "magic-string": "^0.30.21",
-        "source-map-js": "^1.2.1",
-        "tailwindcss": "4.1.17"
+        "@opentelemetry/api-logs": "0.57.2",
+        "@types/shimmer": "^1.2.0",
+        "import-in-the-middle": "^1.8.1",
+        "require-in-the-middle": "^7.1.1",
+        "semver": "^7.5.2",
+        "shimmer": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz",
-      "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==",
+    "node_modules/@opentelemetry/instrumentation-amqplib": {
+      "version": "0.46.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz",
+      "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
       },
-      "optionalDependencies": {
-        "@tailwindcss/oxide-android-arm64": "4.1.17",
-        "@tailwindcss/oxide-darwin-arm64": "4.1.17",
-        "@tailwindcss/oxide-darwin-x64": "4.1.17",
-        "@tailwindcss/oxide-freebsd-x64": "4.1.17",
-        "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17",
-        "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17",
-        "@tailwindcss/oxide-linux-arm64-musl": "4.1.17",
-        "@tailwindcss/oxide-linux-x64-gnu": "4.1.17",
-        "@tailwindcss/oxide-linux-x64-musl": "4.1.17",
-        "@tailwindcss/oxide-wasm32-wasi": "4.1.17",
-        "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17",
-        "@tailwindcss/oxide-win32-x64-msvc": "4.1.17"
-      }
-    },
-    "node_modules/@tailwindcss/oxide-android-arm64": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz",
-      "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==",
-      "cpu": [
-        "arm64"
-      ],
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-connect": {
+      "version": "0.43.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz",
+      "integrity": "sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "android"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/connect": "3.4.38"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-darwin-arm64": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz",
-      "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==",
-      "cpu": [
-        "arm64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-dataloader": {
+      "version": "0.16.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz",
+      "integrity": "sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-darwin-x64": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz",
-      "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-express": {
+      "version": "0.47.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz",
+      "integrity": "sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-freebsd-x64": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz",
-      "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-fs": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz",
+      "integrity": "sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz",
-      "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==",
-      "cpu": [
-        "arm"
-      ],
+    "node_modules/@opentelemetry/instrumentation-generic-pool": {
+      "version": "0.43.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz",
+      "integrity": "sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz",
-      "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==",
-      "cpu": [
-        "arm64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-graphql": {
+      "version": "0.47.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz",
+      "integrity": "sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz",
-      "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==",
-      "cpu": [
-        "arm64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-hapi": {
+      "version": "0.45.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz",
+      "integrity": "sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz",
-      "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-http": {
+      "version": "0.57.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz",
+      "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.30.1",
+        "@opentelemetry/instrumentation": "0.57.2",
+        "@opentelemetry/semantic-conventions": "1.28.0",
+        "forwarded-parse": "2.1.2",
+        "semver": "^7.5.2"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-linux-x64-musl": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz",
-      "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
+      "license": "Apache-2.0",
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz",
-      "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==",
-      "bundleDependencies": [
-        "@napi-rs/wasm-runtime",
-        "@emnapi/core",
-        "@emnapi/runtime",
-        "@tybys/wasm-util",
-        "@emnapi/wasi-threads",
-        "tslib"
-      ],
-      "cpu": [
-        "wasm32"
-      ],
+    "node_modules/@opentelemetry/instrumentation-http/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "@emnapi/core": "^1.6.0",
-        "@emnapi/runtime": "^1.6.0",
-        "@emnapi/wasi-threads": "^1.1.0",
-        "@napi-rs/wasm-runtime": "^1.0.7",
-        "@tybys/wasm-util": "^0.10.1",
-        "tslib": "^2.4.0"
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
       },
       "engines": {
-        "node": ">=14.0.0"
+        "node": ">=10"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
-      "version": "1.6.0",
+    "node_modules/@opentelemetry/instrumentation-ioredis": {
+      "version": "0.47.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz",
+      "integrity": "sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==",
       "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "optional": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "@emnapi/wasi-threads": "1.1.0",
-        "tslib": "^2.4.0"
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/redis-common": "^0.36.2",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
-      "version": "1.6.0",
+    "node_modules/@opentelemetry/instrumentation-kafkajs": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz",
+      "integrity": "sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==",
       "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "optional": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "tslib": "^2.4.0"
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
-      "version": "1.1.0",
+    "node_modules/@opentelemetry/instrumentation-knex": {
+      "version": "0.44.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz",
+      "integrity": "sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-koa": {
+      "version": "0.47.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz",
+      "integrity": "sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-lru-memoizer": {
+      "version": "0.44.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz",
+      "integrity": "sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-mongodb": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz",
+      "integrity": "sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-mongoose": {
+      "version": "0.46.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz",
+      "integrity": "sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-mysql": {
+      "version": "0.45.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz",
+      "integrity": "sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/mysql": "2.15.26"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-mysql2": {
+      "version": "0.45.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz",
+      "integrity": "sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@opentelemetry/sql-common": "^0.40.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-pg": {
+      "version": "0.51.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz",
+      "integrity": "sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.26.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@opentelemetry/sql-common": "^0.40.1",
+        "@types/pg": "8.6.1",
+        "@types/pg-pool": "2.0.6"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-redis-4": {
+      "version": "0.46.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz",
+      "integrity": "sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/redis-common": "^0.36.2",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-tedious": {
+      "version": "0.18.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz",
+      "integrity": "sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/tedious": "^4.0.14"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-undici": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz",
+      "integrity": "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.8.0",
+        "@opentelemetry/instrumentation": "^0.57.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.7.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@opentelemetry/redis-common": {
+      "version": "0.36.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz",
+      "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/resources": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz",
+      "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.30.1",
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-trace-base": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz",
+      "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.30.1",
+        "@opentelemetry/resources": "1.30.1",
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.41.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz",
+      "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/sql-common": {
+      "version": "0.40.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz",
+      "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.1.0"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
+      "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^2.0.3",
+        "is-glob": "^4.0.3",
+        "node-addon-api": "^7.0.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.5.6",
+        "@parcel/watcher-darwin-arm64": "2.5.6",
+        "@parcel/watcher-darwin-x64": "2.5.6",
+        "@parcel/watcher-freebsd-x64": "2.5.6",
+        "@parcel/watcher-linux-arm-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm-musl": "2.5.6",
+        "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm64-musl": "2.5.6",
+        "@parcel/watcher-linux-x64-glibc": "2.5.6",
+        "@parcel/watcher-linux-x64-musl": "2.5.6",
+        "@parcel/watcher-win32-arm64": "2.5.6",
+        "@parcel/watcher-win32-ia32": "2.5.6",
+        "@parcel/watcher-win32-x64": "2.5.6"
+      }
+    },
+    "node_modules/@parcel/watcher-android-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
+      "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
+      "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-darwin-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
+      "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-freebsd-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
+      "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
+      "cpu": [
+        "x64"
+      ],
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
+      "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
+      "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
+      "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-arm64-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
+      "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+      "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+      "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-arm64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
+      "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-ia32": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
+      "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-win32-x64": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
+      "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher/node_modules/picomatch": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@paulirish/trace_engine": {
+      "version": "0.0.59",
+      "resolved": "https://registry.npmjs.org/@paulirish/trace_engine/-/trace_engine-0.0.59.tgz",
+      "integrity": "sha512-439NUzQGmH+9Y017/xCchBP9571J4bzhpcNhrxorf7r37wcyJZkgUfrUsRL3xl+JDcZ6ORhoFCzCw98c6S3YHw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "legacy-javascript": "latest",
+        "third-party-web": "latest"
+      }
+    },
+    "node_modules/@php-wasm/cli-util": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/cli-util/-/cli-util-3.1.41.tgz",
+      "integrity": "sha512-Tya6tusfES8csh+3oAOY80I3PQaEd3tFKPryIi6tuwSOUQTqN87qIXVBzjxBk1Gmb53ZwYg8q8+Cu6hUGieBpA==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/util": "3.1.41",
+        "fast-xml-parser": "^5.8.0",
+        "jsonc-parser": "3.3.1"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/cli-util/node_modules/jsonc-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@php-wasm/logger": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/logger/-/logger-3.1.41.tgz",
+      "integrity": "sha512-bIX1A/6AeN8mCMHYGAUX9Gf2S3wuDVcM3fsG51OqFiwZCKfKOVJNyPm7YAykofeEUEYsrVHjyWNBv6jHMDF3BA==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node/-/node-3.1.41.tgz",
+      "integrity": "sha512-GDNOkAQuWq+bZmQNHPhOMXklNLQpIyeLmw0m1K0IrL+Z3XAxCBPlnn0om5+bhqO7JjqVlbCb+5T/s2gw1IWBEg==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/cli-util": "3.1.41",
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/node-5-2": "3.1.41",
+        "@php-wasm/node-7-4": "3.1.41",
+        "@php-wasm/node-8-0": "3.1.41",
+        "@php-wasm/node-8-1": "3.1.41",
+        "@php-wasm/node-8-2": "3.1.41",
+        "@php-wasm/node-8-3": "3.1.41",
+        "@php-wasm/node-8-4": "3.1.41",
+        "@php-wasm/node-8-5": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "fs-ext-extra-prebuilt": "2.2.7",
+        "wasm-feature-detect": "1.8.0",
+        "ws": "8.21.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-5-2": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-5-2/-/node-5-2-3.1.41.tgz",
+      "integrity": "sha512-GbtDcymMvPJcrhMbubf7r1xfjNNBYxAXnMzPJb3vrcXiDDDdt8NYan8ekvj3meWJzlqj0spj8SXqO2OleQad4w==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-7-4": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-7-4/-/node-7-4-3.1.41.tgz",
+      "integrity": "sha512-hG4RjCsrXFPKVWTbfyoEXqqOJkAwnHdZcbpTgxn253SnQXOUea94lJXrN3ki6Nk0WauM7MH7PFZbN+0NNjPypQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-0": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-0/-/node-8-0-3.1.41.tgz",
+      "integrity": "sha512-ivN/otcmGtoZWjt1K2IE+810u2rxAQuspay/rRJdXsY9mxiCoo0vTlxcl5v1DJIx2HvsHHfaAjoWcmckb7djAw==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-1": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-1/-/node-8-1-3.1.41.tgz",
+      "integrity": "sha512-H5LhYZMI2qY2j+h6GERPUBnv0+K0oMpaYyVd01BOfvJiJN1KVDAp+tNBnVUq6lPl6lpFjOHl5/9jfvJDXdpt+w==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-2": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-2/-/node-8-2-3.1.41.tgz",
+      "integrity": "sha512-RgPyrCS3yS8ELsthqkjFTqBL98gpxX2B1ZH+en/9TD97h4PvRnAKU3909EwtsXoySTPl6ITcOgB4HuXVjr04pA==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-3": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-3/-/node-8-3-3.1.41.tgz",
+      "integrity": "sha512-CkYlFrBQrjMTvig5noRKX9vYfEbL38AH6ufZ2SpfSlq4Z4n+iIRNJHj/865kTaMagY/J3/4A5QgCH5FCMA3xoQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-4": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-4/-/node-8-4-3.1.41.tgz",
+      "integrity": "sha512-t+c2fu1RiElIZk72arZ3GfjeCCwajtiQAf3YyTJnrGF20PYpnluX0HuwoQNWY3EsP+1X1KhUqYrGXVrC+w8Tiw==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/node-8-5": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/node-8-5/-/node-8-5-3.1.41.tgz",
+      "integrity": "sha512-+cO8/+yqYIkIL01Twwp9yZC+3fxQTF1vrF5ZtMtS4iKjDHip9mKfZNCIx0RNRfXFf2+IOxhWpIvK1fkoOQTaFA==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "wasm-feature-detect": "1.8.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/progress": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/progress/-/progress-3.1.41.tgz",
+      "integrity": "sha512-nwXh9cmXY2j61vI/aBYO78jnAbD2C46HILuDarwKi0FXxkMIQ86z5vCWz7iLbBw42kCtkelC8Lbbd2dY8eDR9w==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/logger": "3.1.41"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/scopes": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/scopes/-/scopes-3.1.41.tgz",
+      "integrity": "sha512-ux7RzRsI6JmBnmx3ZH2XUMXV1AVvOoeSoQz2+cSML4Y+lMRvYZLLr1FW9NE69tyij+XkoXbQ4ed+ktDt31O8qg==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/stream-compression": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.41.tgz",
+      "integrity": "sha512-g6u6LhSVXRtC7TdaA+77iYOi58IjTBGL7YoN1bwVwMETz6TJEiXu6u+PPHOGbVDoriz8+Z6JtxOayM3HykT3WQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/util": "3.1.41"
+      }
+    },
+    "node_modules/@php-wasm/universal": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.41.tgz",
+      "integrity": "sha512-T2vTpE0tdDi+2XOng/ZOHj5D2qEepnhSg2f4y64d7y6/Z+QUpzm9uyvxlsUVoiFhwklWC++Bq5dX3eXEI2lVUQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/progress": "3.1.41",
+        "@php-wasm/stream-compression": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "ini": "4.1.2"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/universal/node_modules/ini": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz",
+      "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@php-wasm/util": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/util/-/util-3.1.41.tgz",
+      "integrity": "sha512-+t2nsvnYlfzY9LRpTXXrGa71LtTeujEpASfIAUhpidSc7CJTla9Oab0dQ+2slq6o4PU9Oc5NRMrz768iDp08eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/web-service-worker": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/web-service-worker/-/web-service-worker-3.1.41.tgz",
+      "integrity": "sha512-Ubx370HnZs/qu0q5GyuzEbjn/V50Gu+r8yObls/s6tnXiw0wVDhV94upLfsgjOmosGacxFNDVesmgwJTuKyEjg==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/scopes": "3.1.41",
+        "@php-wasm/universal": "3.1.41"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@php-wasm/xdebug-bridge": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@php-wasm/xdebug-bridge/-/xdebug-bridge-3.1.41.tgz",
+      "integrity": "sha512-HfAILIRbzXkw0kMuenExtU3UCEoM5wcyxgwZlL5+DyaDLqjjUU71lgfmq/BNctk+9I+5U64SN50c2OXfikkcoQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "ws": "8.21.0",
+        "xml2js": "0.6.2",
+        "yargs": "17.7.2"
+      },
+      "bin": {
+        "xdebug-bridge": "xdebug-bridge.js"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "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"
+      }
+    },
+    "node_modules/@pkgr/core": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz",
+      "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/pkgr"
+      }
+    },
+    "node_modules/@playwright/test": {
+      "version": "1.61.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz",
+      "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "playwright": "1.61.1"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@pmmmwh/react-refresh-webpack-plugin": {
+      "version": "0.5.15",
+      "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz",
+      "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-html": "^0.0.9",
+        "core-js-pure": "^3.23.3",
+        "error-stack-parser": "^2.0.6",
+        "html-entities": "^2.1.0",
+        "loader-utils": "^2.0.4",
+        "schema-utils": "^4.2.0",
+        "source-map": "^0.7.3"
+      },
+      "engines": {
+        "node": ">= 10.13"
+      },
+      "peerDependencies": {
+        "@types/webpack": "4.x || 5.x",
+        "react-refresh": ">=0.10.0 <1.0.0",
+        "sockjs-client": "^1.4.0",
+        "type-fest": ">=0.17.0 <5.0.0",
+        "webpack": ">=4.43.0 <6.0.0",
+        "webpack-dev-server": "3.x || 4.x || 5.x",
+        "webpack-hot-middleware": "2.x",
+        "webpack-plugin-serve": "0.x || 1.x"
+      },
+      "peerDependenciesMeta": {
+        "@types/webpack": {
+          "optional": true
+        },
+        "sockjs-client": {
+          "optional": true
+        },
+        "type-fest": {
+          "optional": true
+        },
+        "webpack-dev-server": {
+          "optional": true
+        },
+        "webpack-hot-middleware": {
+          "optional": true
+        },
+        "webpack-plugin-serve": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@pnpm/config.env-replace": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+      "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.22.0"
+      }
+    },
+    "node_modules/@pnpm/network.ca-file": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+      "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "4.2.10"
+      },
+      "engines": {
+        "node": ">=12.22.0"
+      }
+    },
+    "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@pnpm/npm-conf": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.3.tgz",
+      "integrity": "sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pnpm/config.env-replace": "^1.1.0",
+        "@pnpm/network.ca-file": "^1.0.1",
+        "config-chain": "^1.1.11"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@polka/url": {
+      "version": "1.0.0-next.25",
+      "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
+      "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@prisma/instrumentation": {
+      "version": "6.11.1",
+      "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.11.1.tgz",
+      "integrity": "sha512-mrZOev24EDhnefmnZX7WVVT7v+r9LttPRqf54ONvj6re4XMF7wFTpK2tLJi4XHB7fFp/6xhYbgRel8YV7gQiyA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.8"
+      }
+    },
+    "node_modules/@puppeteer/browsers": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz",
+      "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "extract-zip": "^2.0.1",
+        "progress": "^2.0.3",
+        "proxy-agent": "^6.5.0",
+        "semver": "^7.6.3",
+        "tar-fs": "^3.0.6",
+        "unbzip2-stream": "^1.4.3",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "browsers": "lib/cjs/main-cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@puppeteer/browsers/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@rtsao/scc": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+      "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@sec-ant/readable-stream": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+      "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@semantic-release/changelog": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz",
+      "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@semantic-release/error": "^3.0.0",
+        "aggregate-error": "^3.0.0",
+        "fs-extra": "^11.0.0",
+        "lodash": "^4.17.4"
+      },
+      "engines": {
+        "node": ">=14.17"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=18.0.0"
+      }
+    },
+    "node_modules/@semantic-release/commit-analyzer": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz",
+      "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "conventional-changelog-angular": "^8.0.0",
+        "conventional-changelog-writer": "^8.0.0",
+        "conventional-commits-filter": "^5.0.0",
+        "conventional-commits-parser": "^6.0.0",
+        "debug": "^4.0.0",
+        "import-from-esm": "^2.0.0",
+        "lodash-es": "^4.17.21",
+        "micromatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=20.8.1"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=20.1.0"
+      }
+    },
+    "node_modules/@semantic-release/error": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz",
+      "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/@semantic-release/exec": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-6.0.3.tgz",
+      "integrity": "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@semantic-release/error": "^3.0.0",
+        "aggregate-error": "^3.0.0",
+        "debug": "^4.0.0",
+        "execa": "^5.0.0",
+        "lodash": "^4.17.4",
+        "parse-json": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=14.17"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=18.0.0"
+      }
+    },
+    "node_modules/@semantic-release/git": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz",
+      "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@semantic-release/error": "^3.0.0",
+        "aggregate-error": "^3.0.0",
+        "debug": "^4.0.0",
+        "dir-glob": "^3.0.0",
+        "execa": "^5.0.0",
+        "lodash": "^4.17.4",
+        "micromatch": "^4.0.0",
+        "p-reduce": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.17"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=18.0.0"
+      }
+    },
+    "node_modules/@semantic-release/github": {
+      "version": "12.0.8",
+      "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-12.0.8.tgz",
+      "integrity": "sha512-tej5AAgK5X9wHRoDmYhecMXEHEkFeGOY1XsEblKxu8pIQwahzf1STYyr7iPU6Lpbg6C5I3N2w/ocXrBo+L7jhw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@octokit/core": "^7.0.0",
+        "@octokit/plugin-paginate-rest": "^14.0.0",
+        "@octokit/plugin-retry": "^8.0.0",
+        "@octokit/plugin-throttling": "^11.0.0",
+        "@semantic-release/error": "^4.0.0",
+        "aggregate-error": "^5.0.0",
+        "debug": "^4.3.4",
+        "dir-glob": "^3.0.1",
+        "http-proxy-agent": "^9.0.0",
+        "https-proxy-agent": "^9.0.0",
+        "issue-parser": "^7.0.0",
+        "lodash-es": "^4.17.21",
+        "mime": "^4.0.0",
+        "p-filter": "^4.0.0",
+        "tinyglobby": "^0.2.14",
+        "undici": "^7.0.0",
+        "url-join": "^5.0.0"
+      },
+      "engines": {
+        "node": "^22.14.0 || >= 24.10.0"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=24.1.0"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/@semantic-release/error": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+      "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/agent-base": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz",
+      "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/aggregate-error": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+      "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clean-stack": "^5.2.0",
+        "indent-string": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/clean-stack": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz",
+      "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "escape-string-regexp": "5.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/http-proxy-agent": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz",
+      "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "9.0.0",
+        "debug": "^4.3.4",
+        "proxy-agent-negotiate": "1.1.0"
+      },
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/https-proxy-agent": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz",
+      "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "9.0.0",
+        "debug": "^4.3.4",
+        "proxy-agent-negotiate": "1.1.0"
+      },
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/indent-string": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+      "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/github/node_modules/mime": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+      "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
+      "dev": true,
+      "funding": [
+        "https://github.com/sponsors/broofa"
+      ],
+      "license": "MIT",
+      "bin": {
+        "mime": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@semantic-release/npm": {
+      "version": "13.1.5",
+      "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-13.1.5.tgz",
+      "integrity": "sha512-Hq5UxzoatN3LHiq2rTsWS54nCdqJHlsssGERCo8WlvdfFA9LoN0vO+OuKVSjtNapIc/S8C2LBj206wKLHg62mg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@actions/core": "^3.0.0",
+        "@semantic-release/error": "^4.0.0",
+        "aggregate-error": "^5.0.0",
+        "env-ci": "^11.2.0",
+        "execa": "^9.0.0",
+        "fs-extra": "^11.0.0",
+        "lodash-es": "^4.17.21",
+        "nerf-dart": "^1.0.0",
+        "normalize-url": "^9.0.0",
+        "npm": "^11.6.2",
+        "rc": "^1.2.8",
+        "read-pkg": "^10.0.0",
+        "registry-auth-token": "^5.0.0",
+        "semver": "^7.1.2",
+        "tempy": "^3.0.0"
+      },
+      "engines": {
+        "node": "^22.14.0 || >= 24.10.0"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=20.1.0"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+      "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/aggregate-error": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+      "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clean-stack": "^5.2.0",
+        "indent-string": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/clean-stack": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz",
+      "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "escape-string-regexp": "5.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/execa": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz",
+      "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/merge-streams": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "figures": "^6.1.0",
+        "get-stream": "^9.0.0",
+        "human-signals": "^8.0.1",
+        "is-plain-obj": "^4.1.0",
+        "is-stream": "^4.0.1",
+        "npm-run-path": "^6.0.0",
+        "pretty-ms": "^9.2.0",
+        "signal-exit": "^4.1.0",
+        "strip-final-newline": "^4.0.0",
+        "yoctocolors": "^2.1.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/get-stream": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+      "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sec-ant/readable-stream": "^0.4.1",
+        "is-stream": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/hosted-git-info": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz",
+      "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^11.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/human-signals": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
+      "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/indent-string": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+      "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/is-plain-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/is-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+      "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/lru-cache": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+      "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/normalize-package-data": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz",
+      "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^9.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/normalize-url": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-9.0.1.tgz",
+      "integrity": "sha512-ARftfC5HdUNu9jJeL8pHj8debUIHA2b91FizCoMzY4lG6dDX13jdvTK0TBe24IBDRf2HvJSzzwEPvmbkQWHRSg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/npm-run-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
+      "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0",
+        "unicorn-magic": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/parse-json": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
+      "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "index-to-position": "^1.1.0",
+        "type-fest": "^4.39.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/parse-json/node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/read-pkg": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz",
+      "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.4",
+        "normalize-package-data": "^8.0.0",
+        "parse-json": "^8.3.0",
+        "type-fest": "^5.4.4",
+        "unicorn-magic": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/read-pkg/node_modules/unicorn-magic": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+      "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/strip-final-newline": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+      "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/npm/node_modules/type-fest": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz",
+      "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "dependencies": {
+        "tagged-tag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator": {
+      "version": "14.1.1",
+      "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.1.1.tgz",
+      "integrity": "sha512-Pbd2e2XRMUD0OxehHpgd5/YghsE76cddkRHSoDvKLK+OCy4Ewxn49rWR631MEUU01lgwF/uyVXvbnVuu6+Z6VA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "conventional-changelog-angular": "^8.0.0",
+        "conventional-changelog-writer": "^8.0.0",
+        "conventional-commits-filter": "^5.0.0",
+        "conventional-commits-parser": "^6.0.0",
+        "debug": "^4.0.0",
+        "import-from-esm": "^2.0.0",
+        "lodash-es": "^4.17.21",
+        "read-package-up": "^11.0.0"
+      },
+      "engines": {
+        "node": ">=20.8.1"
+      },
+      "peerDependencies": {
+        "semantic-release": ">=20.1.0"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/hosted-git-info": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
+      "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^10.0.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/normalize-package-data": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
+      "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^7.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/parse-json": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
+      "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "index-to-position": "^1.1.0",
+        "type-fest": "^4.39.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/read-package-up": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
+      "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up-simple": "^1.0.0",
+        "read-pkg": "^9.0.0",
+        "type-fest": "^4.6.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
+      "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.3",
+        "normalize-package-data": "^6.0.0",
+        "parse-json": "^8.0.0",
+        "type-fest": "^4.6.0",
+        "unicorn-magic": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@semantic-release/release-notes-generator/node_modules/unicorn-magic": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
+      "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@sentry/core": {
+      "version": "9.47.1",
+      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.47.1.tgz",
+      "integrity": "sha512-KX62+qIt4xgy8eHKHiikfhz2p5fOciXd0Cl+dNzhgPFq8klq4MGMNaf148GB3M/vBqP4nw/eFvRMAayFCgdRQw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@sentry/node": {
+      "version": "9.47.1",
+      "resolved": "https://registry.npmjs.org/@sentry/node/-/node-9.47.1.tgz",
+      "integrity": "sha512-CDbkasBz3fnWRKSFs6mmaRepM2pa+tbZkrqhPWifFfIkJDidtVW40p6OnquTvPXyPAszCnDZRnZT14xyvNmKPQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.30.1",
+        "@opentelemetry/core": "^1.30.1",
+        "@opentelemetry/instrumentation": "^0.57.2",
+        "@opentelemetry/instrumentation-amqplib": "^0.46.1",
+        "@opentelemetry/instrumentation-connect": "0.43.1",
+        "@opentelemetry/instrumentation-dataloader": "0.16.1",
+        "@opentelemetry/instrumentation-express": "0.47.1",
+        "@opentelemetry/instrumentation-fs": "0.19.1",
+        "@opentelemetry/instrumentation-generic-pool": "0.43.1",
+        "@opentelemetry/instrumentation-graphql": "0.47.1",
+        "@opentelemetry/instrumentation-hapi": "0.45.2",
+        "@opentelemetry/instrumentation-http": "0.57.2",
+        "@opentelemetry/instrumentation-ioredis": "0.47.1",
+        "@opentelemetry/instrumentation-kafkajs": "0.7.1",
+        "@opentelemetry/instrumentation-knex": "0.44.1",
+        "@opentelemetry/instrumentation-koa": "0.47.1",
+        "@opentelemetry/instrumentation-lru-memoizer": "0.44.1",
+        "@opentelemetry/instrumentation-mongodb": "0.52.0",
+        "@opentelemetry/instrumentation-mongoose": "0.46.1",
+        "@opentelemetry/instrumentation-mysql": "0.45.1",
+        "@opentelemetry/instrumentation-mysql2": "0.45.2",
+        "@opentelemetry/instrumentation-pg": "0.51.1",
+        "@opentelemetry/instrumentation-redis-4": "0.46.1",
+        "@opentelemetry/instrumentation-tedious": "0.18.1",
+        "@opentelemetry/instrumentation-undici": "0.10.1",
+        "@opentelemetry/resources": "^1.30.1",
+        "@opentelemetry/sdk-trace-base": "^1.30.1",
+        "@opentelemetry/semantic-conventions": "^1.34.0",
+        "@prisma/instrumentation": "6.11.1",
+        "@sentry/core": "9.47.1",
+        "@sentry/node-core": "9.47.1",
+        "@sentry/opentelemetry": "9.47.1",
+        "import-in-the-middle": "^1.14.2",
+        "minimatch": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@sentry/node-core": {
+      "version": "9.47.1",
+      "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-9.47.1.tgz",
+      "integrity": "sha512-7TEOiCGkyShJ8CKtsri9lbgMCbB+qNts2Xq37itiMPN2m+lIukK3OX//L8DC5nfKYZlgikrefS63/vJtm669hQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sentry/core": "9.47.1",
+        "@sentry/opentelemetry": "9.47.1",
+        "import-in-the-middle": "^1.14.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/core": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/instrumentation": ">=0.57.1 <1",
+        "@opentelemetry/resources": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/semantic-conventions": "^1.34.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/brace-expansion": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
+      "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@sentry/opentelemetry": {
+      "version": "9.47.1",
+      "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-9.47.1.tgz",
+      "integrity": "sha512-STtFpjF7lwzeoedDJV+5XA6P89BfmFwFftmHSGSe3UTI8z8IoiR5yB6X2vCjSPvXlfeOs13qCNNCEZyznxM8Xw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sentry/core": "9.47.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/core": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0",
+        "@opentelemetry/semantic-conventions": "^1.34.0"
+      }
+    },
+    "node_modules/@simple-libs/stream-utils": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz",
+      "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://ko-fi.com/dangreen"
+      }
+    },
+    "node_modules/@sinclair/typebox": {
+      "version": "0.27.10",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz",
+      "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@sindresorhus/merge-streams": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+      "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@sinonjs/commons": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/fake-timers": {
+      "version": "10.3.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+      "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@sinonjs/commons": "^3.0.0"
+      }
+    },
+    "node_modules/@standard-schema/spec": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+      "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@stylistic/stylelint-plugin": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.3.tgz",
+      "integrity": "sha512-85fsmzgsIVmyG3/GFrjuYj6Cz8rAM7IZiPiXCMiSMfoDOC1lOrzrXPDk24WqviAghnPqGpx8b0caK2PuewWGFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.1",
+        "@csstools/css-tokenizer": "^3.0.1",
+        "@csstools/media-query-list-parser": "^3.0.1",
+        "is-plain-object": "^5.0.0",
+        "postcss": "^8.4.41",
+        "postcss-selector-parser": "^6.1.2",
+        "postcss-value-parser": "^4.2.0",
+        "style-search": "^0.1.0"
+      },
+      "engines": {
+        "node": "^18.12 || >=20.9"
+      },
+      "peerDependencies": {
+        "stylelint": "^16.8.0"
+      }
+    },
+    "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/@svgr/babel-plugin-add-jsx-attribute": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
+      "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
+      "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
+      "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
+      "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
+      "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
+      "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
+      "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-plugin-transform-svg-component": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
+      "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/babel-preset": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
+      "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
+        "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
+        "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
+        "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
+        "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
+        "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
+        "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
+        "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@svgr/core": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
+      "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+      "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "tslib": "^2.4.0"
+        "@babel/core": "^7.21.3",
+        "@svgr/babel-preset": "8.1.0",
+        "camelcase": "^6.2.0",
+        "cosmiconfig": "^8.1.3",
+        "snake-case": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
-      "version": "1.0.7",
+    "node_modules/@svgr/hast-util-to-babel-ast": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
+      "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
-      "optional": true,
       "dependencies": {
-        "@emnapi/core": "^1.5.0",
-        "@emnapi/runtime": "^1.5.0",
-        "@tybys/wasm-util": "^0.10.1"
+        "@babel/types": "^7.21.3",
+        "entities": "^4.4.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
-      "version": "0.10.1",
+    "node_modules/@svgr/plugin-jsx": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
+      "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
-      "optional": true,
       "dependencies": {
-        "tslib": "^2.4.0"
+        "@babel/core": "^7.21.3",
+        "@svgr/babel-preset": "8.1.0",
+        "@svgr/hast-util-to-babel-ast": "8.0.0",
+        "svg-parser": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@svgr/core": "*"
       }
     },
-    "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
-      "version": "2.8.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "0BSD",
-      "optional": true
-    },
-    "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz",
-      "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==",
-      "cpu": [
-        "arm64"
-      ],
+    "node_modules/@svgr/plugin-svgo": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz",
+      "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==",
       "dev": true,
       "license": "MIT",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
+      "dependencies": {
+        "cosmiconfig": "^8.1.3",
+        "deepmerge": "^4.3.1",
+        "svgo": "^3.0.2"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
+      },
+      "peerDependencies": {
+        "@svgr/core": "*"
       }
     },
-    "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz",
-      "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/@svgr/webpack": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz",
+      "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==",
       "dev": true,
       "license": "MIT",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
+      "dependencies": {
+        "@babel/core": "^7.21.3",
+        "@babel/plugin-transform-react-constant-elements": "^7.21.3",
+        "@babel/preset-env": "^7.20.2",
+        "@babel/preset-react": "^7.18.6",
+        "@babel/preset-typescript": "^7.21.0",
+        "@svgr/core": "8.1.0",
+        "@svgr/plugin-jsx": "8.1.0",
+        "@svgr/plugin-svgo": "8.1.0"
+      },
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/gregberge"
       }
     },
-    "node_modules/@tailwindcss/postcss": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz",
-      "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==",
+    "node_modules/@szmarczak/http-timer": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@alloc/quick-lru": "^5.2.0",
-        "@tailwindcss/node": "4.1.17",
-        "@tailwindcss/oxide": "4.1.17",
-        "postcss": "^8.4.41",
-        "tailwindcss": "4.1.17"
+        "defer-to-connect": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
       }
     },
     "node_modules/@tannin/compile": {
@@ -4804,15 +7937,12 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@tootallnate/once": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
-      "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+    "node_modules/@tannin/sprintf": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz",
+      "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==",
       "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10"
-      }
+      "license": "MIT"
     },
     "node_modules/@tootallnate/quickjs-emscripten": {
       "version": "0.23.0",
@@ -4831,6 +7961,24 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/@tybys/wasm-util": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+      "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@types/aws-lambda": {
+      "version": "8.10.162",
+      "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz",
+      "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/babel__core": {
       "version": "7.20.5",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -4846,9 +7994,9 @@
       }
     },
     "node_modules/@types/babel__generator": {
-      "version": "7.6.8",
-      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
-      "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+      "version": "7.27.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+      "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4867,13 +8015,13 @@
       }
     },
     "node_modules/@types/babel__traverse": {
-      "version": "7.20.6",
-      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
-      "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+      "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/types": "^7.20.7"
+        "@babel/types": "^7.28.2"
       }
     },
     "node_modules/@types/body-parser": {
@@ -4897,6 +8045,13 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/btoa-lite": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
+      "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/cacheable-request": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@@ -4953,10 +8108,17 @@
         "@types/estree": "*"
       }
     },
+    "node_modules/@types/esrecurse": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
-      "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+      "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
       "dev": true,
       "license": "MIT"
     },
@@ -4986,17 +8148,6 @@
         "@types/send": "*"
       }
     },
-    "node_modules/@types/glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/minimatch": "*",
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/graceful-fs": {
       "version": "4.1.9",
       "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -5059,9 +8210,9 @@
       }
     },
     "node_modules/@types/jsdom": {
-      "version": "20.0.1",
-      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
-      "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
+      "version": "21.1.7",
+      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
+      "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -5084,6 +8235,17 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "9.0.10",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+      "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*",
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/keyv": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -5111,13 +8273,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/minimatch": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
-      "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@types/minimist": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@@ -5125,14 +8280,38 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/mousetrap": {
+      "version": "1.6.15",
+      "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz",
+      "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+      "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/mysql": {
+      "version": "2.15.26",
+      "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz",
+      "integrity": "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/node": {
-      "version": "20.14.10",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
-      "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
+      "version": "20.19.43",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
+      "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "undici-types": "~5.26.4"
+        "undici-types": "~6.21.0"
       }
     },
     "node_modules/@types/node-forge": {
@@ -5159,6 +8338,28 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/pg": {
+      "version": "8.6.1",
+      "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz",
+      "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "pg-protocol": "*",
+        "pg-types": "^2.2.0"
+      }
+    },
+    "node_modules/@types/pg-pool": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz",
+      "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/pg": "*"
+      }
+    },
     "node_modules/@types/prop-types": {
       "version": "15.7.15",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
@@ -5180,13 +8381,13 @@
       "license": "MIT"
     },
     "node_modules/@types/react": {
-      "version": "18.3.26",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
-      "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
+      "version": "18.3.31",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
+      "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
       "license": "MIT",
       "dependencies": {
         "@types/prop-types": "*",
-        "csstype": "^3.0.2"
+        "csstype": "^3.2.2"
       }
     },
     "node_modules/@types/react-dom": {
@@ -5215,13 +8416,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/semver": {
-      "version": "7.7.0",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
-      "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@types/send": {
       "version": "0.17.4",
       "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@@ -5255,6 +8449,13 @@
         "@types/send": "*"
       }
     },
+    "node_modules/@types/shimmer": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
+      "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/sockjs": {
       "version": "0.3.36",
       "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
@@ -5265,13 +8466,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/source-list-map": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz",
-      "integrity": "sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@types/stack-utils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@@ -5279,12 +8473,15 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/tapable": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz",
-      "integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==",
+    "node_modules/@types/tedious": {
+      "version": "4.0.14",
+      "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz",
+      "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
     },
     "node_modules/@types/tough-cookie": {
       "version": "4.0.5",
@@ -5293,26 +8490,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/uglify-js": {
-      "version": "3.17.5",
-      "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.5.tgz",
-      "integrity": "sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/@types/uglify-js/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@types/unist": {
       "version": "2.0.10",
       "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
@@ -5320,43 +8497,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/webpack": {
-      "version": "4.41.38",
-      "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.38.tgz",
-      "integrity": "sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/tapable": "^1",
-        "@types/uglify-js": "*",
-        "@types/webpack-sources": "*",
-        "anymatch": "^3.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/@types/webpack-sources": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.3.tgz",
-      "integrity": "sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/source-list-map": "*",
-        "source-map": "^0.7.3"
-      }
-    },
-    "node_modules/@types/webpack/node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@types/ws": {
       "version": "8.5.10",
       "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
@@ -5368,9 +8508,9 @@
       }
     },
     "node_modules/@types/yargs": {
-      "version": "17.0.32",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
-      "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
+      "version": "17.0.35",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
+      "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -5396,137 +8536,159 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
-      "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz",
+      "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/type-utils": "6.21.0",
-        "@typescript-eslint/utils": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4",
-        "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@typescript-eslint/scope-manager": "8.62.1",
+        "@typescript-eslint/type-utils": "8.62.1",
+        "@typescript-eslint/utils": "8.62.1",
+        "@typescript-eslint/visitor-keys": "8.62.1",
+        "ignore": "^7.0.5",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^2.5.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "@typescript-eslint/parser": "^8.62.1",
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
-      "version": "7.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
-      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
+      "license": "MIT",
       "engines": {
-        "node": ">=10"
+        "node": ">= 4"
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
-      "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz",
+      "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==",
       "dev": true,
-      "license": "BSD-2-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4"
+        "@typescript-eslint/scope-manager": "8.62.1",
+        "@typescript-eslint/types": "8.62.1",
+        "@typescript-eslint/typescript-estree": "8.62.1",
+        "@typescript-eslint/visitor-keys": "8.62.1",
+        "debug": "^4.4.3"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz",
+      "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.62.1",
+        "@typescript-eslint/types": "^8.62.1",
+        "debug": "^4.4.3"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
-      "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz",
+      "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0"
+        "@typescript-eslint/types": "8.62.1",
+        "@typescript-eslint/visitor-keys": "8.62.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/tsconfig-utils": {
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz",
+      "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==",
+      "dev": true,
+      "license": "MIT",
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
-      "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz",
+      "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "@typescript-eslint/utils": "6.21.0",
-        "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "@typescript-eslint/types": "8.62.1",
+        "@typescript-eslint/typescript-estree": "8.62.1",
+        "@typescript-eslint/utils": "8.62.1",
+        "debug": "^4.4.3",
+        "ts-api-utils": "^2.5.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
-      "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz",
+      "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -5534,38 +8696,76 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
-      "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz",
+      "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==",
       "dev": true,
-      "license": "BSD-2-Clause",
+      "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4",
-        "globby": "^11.1.0",
-        "is-glob": "^4.0.3",
-        "minimatch": "9.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "@typescript-eslint/project-service": "8.62.1",
+        "@typescript-eslint/tsconfig-utils": "8.62.1",
+        "@typescript-eslint/types": "8.62.1",
+        "@typescript-eslint/visitor-keys": "8.62.1",
+        "debug": "^4.4.3",
+        "minimatch": "^10.2.2",
+        "semver": "^7.7.3",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.5.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+      "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "brace-expansion": "^5.0.5"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
-      "version": "7.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
-      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -5576,81 +8776,372 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
-      "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz",
+      "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "semver": "^7.5.4"
+        "@eslint-community/eslint-utils": "^4.9.1",
+        "@typescript-eslint/scope-manager": "8.62.1",
+        "@typescript-eslint/types": "8.62.1",
+        "@typescript-eslint/typescript-estree": "8.62.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
       }
     },
-    "node_modules/@typescript-eslint/utils/node_modules/semver": {
-      "version": "7.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
-      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz",
+      "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.62.1",
+        "eslint-visitor-keys": "^5.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
       }
     },
-    "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
-      "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz",
+      "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-android-arm64": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz",
+      "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-darwin-arm64": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz",
+      "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-darwin-x64": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz",
+      "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-freebsd-x64": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz",
+      "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz",
+      "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz",
+      "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz",
+      "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz",
+      "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-loong64-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz",
+      "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-loong64-musl": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz",
+      "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz",
+      "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz",
+      "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz",
+      "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz",
+      "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz",
+      "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz",
+      "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-openharmony-arm64": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz",
+      "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz",
+      "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==",
+      "cpu": [
+        "wasm32"
+      ],
       "dev": true,
       "license": "MIT",
+      "optional": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "eslint-visitor-keys": "^3.4.1"
+        "@emnapi/core": "1.10.0",
+        "@emnapi/runtime": "1.10.0",
+        "@napi-rs/wasm-runtime": "^1.1.4"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
+        "node": ">=14.0.0"
       }
     },
-    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+    "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
+      "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
+      "cpu": [
+        "arm64"
+      ],
       "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
     },
-    "node_modules/@ungap/structured-clone": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
-      "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+    "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
+      "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
+      "cpu": [
+        "ia32"
+      ],
       "dev": true,
-      "license": "ISC"
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
+      "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.14.1",
@@ -5860,129 +9351,166 @@
         }
       }
     },
-    "node_modules/@wordpress/api-fetch": {
-      "version": "6.55.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.55.0.tgz",
-      "integrity": "sha512-1HrCUsJdeRY5Y0IjplotINwqMRO81e7O7VhBScuKk7iOuDm/E1ioKv2uLGnPNWziYu+Zf025byxOqVzXDyM2gw==",
+    "node_modules/@wordpress/babel-preset-default": {
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.50.0.tgz",
+      "integrity": "sha512-nXF+cu0NA9lk4GPO+/iv3mMt1TV9zzjMalfaNPfafWz5VDGW68h82Le8wqclTewex/99kYWl12kHwDIx66hw6Q==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "^7.16.0",
-        "@wordpress/i18n": "^4.58.0",
-        "@wordpress/url": "^3.59.0"
+        "@babel/core": "^7.25.7",
+        "@babel/plugin-syntax-import-attributes": "^7.26.0",
+        "@babel/plugin-transform-react-jsx": "^7.25.7",
+        "@babel/plugin-transform-runtime": "^7.25.7",
+        "@babel/preset-env": "^7.25.7",
+        "@babel/preset-typescript": "^7.25.7",
+        "@wordpress/browserslist-config": "^6.50.0",
+        "@wordpress/warning": "^3.50.0",
+        "browserslist": "^4.21.10",
+        "core-js": "^3.31.0",
+        "react": "^18.3.1"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
-    "node_modules/@wordpress/babel-plugin-import-jsx-pragma": {
-      "version": "4.41.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/babel-plugin-import-jsx-pragma/-/babel-plugin-import-jsx-pragma-4.41.0.tgz",
-      "integrity": "sha512-hYxj2Uobxk86ctlfaJou9v13XqXZ30yx4ZwRNu5cH5/LWXe2MIXBTPv7dUk6wqN/qFOjsFvP9jCB0NsW6MnkrA==",
+    "node_modules/@wordpress/base-styles": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-10.2.0.tgz",
+      "integrity": "sha512-iDPbyyeUnxLFq4ec+03x87jT8lRoBzK2rDoEg24l2KNAR9ZJ2kwI0z2E/wW0fOZC37A7qr377KJslPHKLtMgyQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "engines": {
-        "node": ">=14"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.12.9"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
-    "node_modules/@wordpress/babel-preset-default": {
-      "version": "7.42.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-7.42.0.tgz",
-      "integrity": "sha512-AWSxWuEuzazt/nWomKiaVhYQeXuqxTniPCKhvks58wB3P4UXvSe3hRnO+nujz20IuxIk2xHT6x47HgpDZy30jw==",
+    "node_modules/@wordpress/browserslist-config": {
+      "version": "6.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.50.0.tgz",
+      "integrity": "sha512-/5Zn/uIvVw37iLJRa/HhqHQ/DJq4KWjEXTkFQ/NpCNFQm6ll54uDgYZ9f0tWQM7ORCzCU32z/IJ8ujZaMLA9nQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
-      "dependencies": {
-        "@babel/core": "^7.16.0",
-        "@babel/plugin-transform-react-jsx": "^7.16.0",
-        "@babel/plugin-transform-runtime": "^7.16.0",
-        "@babel/preset-env": "^7.16.0",
-        "@babel/preset-typescript": "^7.16.0",
-        "@babel/runtime": "^7.16.0",
-        "@wordpress/babel-plugin-import-jsx-pragma": "^4.41.0",
-        "@wordpress/browserslist-config": "^5.41.0",
-        "@wordpress/warning": "^2.58.0",
-        "browserslist": "^4.21.10",
-        "core-js": "^3.31.0",
-        "react": "^18.3.0"
-      },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
-    "node_modules/@wordpress/base-styles": {
-      "version": "4.49.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-4.49.0.tgz",
-      "integrity": "sha512-yFRYqNtd26ULZ0oAHhCu/IcaA0XHI3E7kRCKajZqUvyRQj7YprXnpD3o0/pnwvF6ZFTXzCX8pXHjUc2TIv97ig==",
-      "dev": true,
-      "license": "GPL-2.0-or-later"
-    },
-    "node_modules/@wordpress/browserslist-config": {
-      "version": "5.41.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-5.41.0.tgz",
-      "integrity": "sha512-J7ejzzDpPZddVIiq2YiK8J/pNTJDy3X1s+5ZtwkwklCxBMZJurxf9pEhtbaf7us0Q6c1j8Ubv7Fpx3lqk2ypxA==",
+    "node_modules/@wordpress/compose": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-8.3.0.tgz",
+      "integrity": "sha512-nHrV8mLanqnLO67l+6RkotG9eRgiW1D6uVb919z8wVWwoZVfimFyN1nznH92tdE/xW6SnX37XDoLoLcS9IkwhQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@types/mousetrap": "^1.6.8",
+        "@wordpress/deprecated": "^4.50.0",
+        "@wordpress/dom": "^4.50.0",
+        "@wordpress/element": "^8.2.0",
+        "@wordpress/is-shallow-equal": "^5.50.0",
+        "@wordpress/keycodes": "^4.50.0",
+        "@wordpress/priority-queue": "^3.50.0",
+        "@wordpress/private-apis": "^1.50.0",
+        "@wordpress/undo-manager": "^1.50.0",
+        "change-case": "^4.1.2",
+        "mousetrap": "^1.6.5",
+        "use-memo-one": "^1.1.1"
+      },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      },
+      "peerDependencies": {
+        "@types/react": "^18.3.27",
+        "react": "^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
       }
     },
     "node_modules/@wordpress/dependency-extraction-webpack-plugin": {
-      "version": "5.9.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-5.9.0.tgz",
-      "integrity": "sha512-hXbCkbG1XES47t7hFSETRrLfaRSPyQPlCnhlCx7FfhYFD0wh1jVArApXX5dD+A6wTrayXX/a16MpfaNqE662XA==",
+      "version": "6.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-6.50.0.tgz",
+      "integrity": "sha512-YtYtLkAUdLXTCFwLxB/RbpBb19egvOvrEk/aZWxSXBT+sg6fNvTgYvkRMOqaVjja0CWDHGm9DJosZOnvAxuCDQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "json2php": "^0.0.7"
+        "json2php": "^0.0.9"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "webpack": "^5.0.0"
       }
     },
+    "node_modules/@wordpress/deprecated": {
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.50.0.tgz",
+      "integrity": "sha512-TVJavTbunFoCSo2g64EKtGwmDVJhvlSqsc4T1EM4aBwsYGIJPjHcAzh6KS8Z171QXFnBxZ7We8CCDAvOFU9iaA==",
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@wordpress/hooks": "^4.50.0"
+      },
+      "engines": {
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      }
+    },
+    "node_modules/@wordpress/dom": {
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.50.0.tgz",
+      "integrity": "sha512-2Mognc1rwe1+tlIfbSeZPqbuAh6vJagELzw9cLasObiVayWhaG+LCIp0sJz2pPG9ppz8L31gGUgna7zrSRu85Q==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@wordpress/deprecated": "^4.50.0"
+      },
+      "engines": {
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      }
+    },
     "node_modules/@wordpress/e2e-test-utils-playwright": {
-      "version": "0.26.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-0.26.0.tgz",
-      "integrity": "sha512-4KFyQ3IsYIJaIvOQ1qhAHhRISs9abNToF/bktfMNxQiEJsmbNn7lq/IbaY+shqwdBWVg8TQtLcL4MpSl0ISaxQ==",
+      "version": "1.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.50.0.tgz",
+      "integrity": "sha512-P55NxY86M8lNnAbEPmpEswbxBoB7BWVhg67KOLRsGwLMeJ4qHbBngmgBIUt72IAH3H2UWVq7jZ1Xm/J1ENOMoQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@wordpress/api-fetch": "^6.55.0",
-        "@wordpress/keycodes": "^3.58.0",
-        "@wordpress/url": "^3.59.0",
         "change-case": "^4.1.2",
-        "form-data": "^4.0.0",
         "get-port": "^5.1.1",
-        "lighthouse": "^10.4.0",
+        "lighthouse": "^12.2.2",
         "mime": "^3.0.0",
-        "web-vitals": "^3.5.0"
+        "web-vitals": "^4.2.1"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
-        "@playwright/test": ">=1"
+        "@playwright/test": ">=1",
+        "@types/node": "^20.17.10"
       }
     },
     "node_modules/@wordpress/element": {
-      "version": "6.32.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.32.0.tgz",
-      "integrity": "sha512-W/Bw6HXzRBJgYYUdoUBUvtjXNWh8dVK8aqFsqpnEJTAiXdU8Ii0wBQ+E49bI/08yGCwsaXrLbQLXqtAiV6leMw==",
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-8.2.0.tgz",
+      "integrity": "sha512-cTnKRHUdZbYAd4RaJiUwhDKdQDDWY7VoGiwektCmhiBQF6WTFYlhsxl/dmHE0Sok3q7PJQwAg538XEb8WuTaew==",
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "7.25.7",
-        "@types/react": "^18.2.79",
-        "@types/react-dom": "^18.2.25",
-        "@wordpress/escape-html": "^3.32.0",
+        "@types/react": "^18.3.27",
+        "@types/react-dom": "^18.3.1",
+        "@wordpress/deprecated": "^4.50.0",
+        "@wordpress/escape-html": "^3.50.0",
         "change-case": "^4.1.2",
         "is-plain-object": "^5.0.0",
-        "react": "^18.3.0",
-        "react-dom": "^18.3.0"
+        "react": "^18.3.1",
+        "react-dom": "^18.3.1"
       },
       "engines": {
         "node": ">=18.12.0",
@@ -5999,23 +9527,24 @@
       }
     },
     "node_modules/@wordpress/env": {
-      "version": "10.32.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-10.32.0.tgz",
-      "integrity": "sha512-GHpcbfh/rUoXd7hwBy84ZBd1uhqQntd9CHrxk5hK/lLM9LULLwrRFC21ZwYwZuNjpdV+DslpsX0N/poe3ybaJQ==",
+      "version": "11.10.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-11.10.0.tgz",
+      "integrity": "sha512-y0q2RLJ3PsWHrRPt7bQbJauF/23JvCkfjBHRefJlGDZ49x1ma+UYQCrhT07G/05CYYJH2Sw6qC3Q4XYBKe7ASA==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
         "@inquirer/prompts": "^7.2.0",
-        "chalk": "^4.0.0",
+        "@wp-playground/cli": "^3.0.48",
+        "adm-zip": "^0.5.9",
+        "chalk": "^4.1.1",
         "copy-dir": "^1.3.0",
+        "cross-spawn": "^7.0.6",
         "docker-compose": "^0.24.3",
-        "extract-zip": "^1.6.7",
         "got": "^11.8.5",
         "js-yaml": "^3.13.1",
         "ora": "^4.0.2",
         "rimraf": "^5.0.10",
-        "simple-git": "^3.5.0",
-        "terminal-link": "^2.0.0",
+        "simple-git": "^3.24.0",
         "yargs": "^17.3.0"
       },
       "bin": {
@@ -6026,32 +9555,6 @@
         "npm": ">=8.19.2"
       }
     },
-    "node_modules/@wordpress/env/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/@wordpress/env/node_modules/extract-zip": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
-      "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "concat-stream": "^1.6.2",
-        "debug": "^2.6.9",
-        "mkdirp": "^0.5.4",
-        "yauzl": "^2.10.0"
-      },
-      "bin": {
-        "extract-zip": "cli.js"
-      }
-    },
     "node_modules/@wordpress/env/node_modules/glob": {
       "version": "10.4.5",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -6089,26 +9592,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@wordpress/env/node_modules/mkdirp": {
-      "version": "0.5.6",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
-      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.6"
-      },
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      }
-    },
-    "node_modules/@wordpress/env/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@wordpress/env/node_modules/rimraf": {
       "version": "5.0.10",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -6126,42 +9609,42 @@
       }
     },
     "node_modules/@wordpress/escape-html": {
-      "version": "3.32.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.32.0.tgz",
-      "integrity": "sha512-pT5wZmg9ob/u8RuSXgfZv8Kfd8zpvtBcCdcFE/UHasjtxJSecxDHFb0uI4eXQrSiTrsthbDZDlK/GIAagmt75Q==",
+      "version": "3.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.50.0.tgz",
+      "integrity": "sha512-EECt++WB1RNPQBwBS7/6rKO4eUNq2xe6nb6Guv6uT+F74DgtM3+aZtNs7JhKFxRMORPtu7T0LhlAOBa4H5CsPQ==",
       "license": "GPL-2.0-or-later",
-      "dependencies": {
-        "@babel/runtime": "7.25.7"
-      },
       "engines": {
         "node": ">=18.12.0",
         "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/eslint-plugin": {
-      "version": "22.20.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-22.20.0.tgz",
-      "integrity": "sha512-mZuEmBLLAOT6koBsXMrFMHQskKs+p+nu1Z/Y/4u1FldRlShdbKSXZG2p9qV3SVnXdSAEa5Cr32kOvkZGacEO/Q==",
+      "version": "25.6.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-25.6.0.tgz",
+      "integrity": "sha512-TZ1M+S8gk0MltuF2WsiNG4sjIUvaurB036v4QL81HM3DsfjPFOpKykyWswWO/iZv/e0f/ryG4O/aDfMbU+oEHg==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/eslint-parser": "7.25.7",
-        "@typescript-eslint/eslint-plugin": "^6.4.1",
-        "@typescript-eslint/parser": "^6.4.1",
-        "@wordpress/babel-preset-default": "^8.34.0",
-        "@wordpress/prettier-config": "^4.34.0",
+        "@babel/eslint-parser": "^7.28.6",
+        "@eslint-community/eslint-plugin-eslint-comments": "^4.7.0",
+        "@eslint/compat": "^2.0.0",
+        "@wordpress/babel-preset-default": "^8.50.0",
+        "@wordpress/prettier-config": "^4.50.0",
+        "@wordpress/theme": "^0.17.0",
         "cosmiconfig": "^7.0.0",
-        "eslint-config-prettier": "^8.3.0",
-        "eslint-plugin-import": "^2.25.2",
-        "eslint-plugin-jest": "^27.4.3",
-        "eslint-plugin-jsdoc": "^46.4.6",
-        "eslint-plugin-jsx-a11y": "^6.5.1",
-        "eslint-plugin-playwright": "^0.15.3",
-        "eslint-plugin-prettier": "^5.0.0",
-        "eslint-plugin-react": "^7.27.0",
-        "eslint-plugin-react-hooks": "^4.3.0",
-        "globals": "^13.12.0",
-        "requireindex": "^1.2.0"
+        "eslint-config-prettier": "^10.0.0",
+        "eslint-import-resolver-typescript": "^4.4.4",
+        "eslint-plugin-import": "^2.31.0",
+        "eslint-plugin-jest": "^28.11.0",
+        "eslint-plugin-jsdoc": "^50.0.0",
+        "eslint-plugin-jsx-a11y": "^6.10.0",
+        "eslint-plugin-playwright": "^2.1.0",
+        "eslint-plugin-prettier": "^5.5.5",
+        "eslint-plugin-react": "^7.37.0",
+        "eslint-plugin-react-hooks": "^7.1.1",
+        "globals": "^16.0.0",
+        "requireindex": "^1.2.0",
+        "typescript-eslint": "^8.57.1"
       },
       "engines": {
         "node": ">=18.12.0",
@@ -6169,7 +9652,7 @@
       },
       "peerDependencies": {
         "@babel/core": ">=7",
-        "eslint": ">=8",
+        "eslint": "^9.0.0 || ^10.0.0",
         "prettier": ">=3",
         "typescript": ">=5"
       },
@@ -6182,103 +9665,6 @@
         }
       }
     },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@babel/plugin-syntax-import-attributes": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
-      "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.25.9"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@babel/plugin-transform-runtime": {
-      "version": "7.25.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.7.tgz",
-      "integrity": "sha512-Y9p487tyTzB0yDYQOtWnC+9HGOuogtP3/wNpun1xJXEEvI6vip59BSBTsHnekZLqxmPcgsrAKt46HAAb//xGhg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.25.7",
-        "@babel/helper-plugin-utils": "^7.25.7",
-        "babel-plugin-polyfill-corejs2": "^0.4.10",
-        "babel-plugin-polyfill-corejs3": "^0.10.6",
-        "babel-plugin-polyfill-regenerator": "^0.6.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@wordpress/babel-preset-default": {
-      "version": "8.34.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.34.0.tgz",
-      "integrity": "sha512-AJQesBDb1LcmwlfpIVkuTu0gwkjgfVdbKG6sqmKfKkjYTac6k+ZJscZqYWgjIK2G0F0/TZwbN6u4otRq+yDAGw==",
-      "dev": true,
-      "license": "GPL-2.0-or-later",
-      "dependencies": {
-        "@babel/core": "7.25.7",
-        "@babel/plugin-syntax-import-attributes": "7.26.0",
-        "@babel/plugin-transform-react-jsx": "7.25.7",
-        "@babel/plugin-transform-runtime": "7.25.7",
-        "@babel/preset-env": "7.25.7",
-        "@babel/preset-typescript": "7.25.7",
-        "@wordpress/browserslist-config": "^6.34.0",
-        "@wordpress/warning": "^3.34.0",
-        "browserslist": "^4.21.10",
-        "core-js": "^3.31.0",
-        "react": "^18.3.0"
-      },
-      "engines": {
-        "node": ">=18.12.0",
-        "npm": ">=8.19.2"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@wordpress/browserslist-config": {
-      "version": "6.34.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.34.0.tgz",
-      "integrity": "sha512-pmcCkqG2jW+UUBSkX7rSZS33mcW6M0fKcJPD40TlK2cUZvECS5TDa2BC/b80PfIsT2kSw+Z9Wv+8eyX6I8HGjQ==",
-      "dev": true,
-      "license": "GPL-2.0-or-later",
-      "engines": {
-        "node": ">=18.12.0",
-        "npm": ">=8.19.2"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@wordpress/prettier-config": {
-      "version": "4.34.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-4.34.0.tgz",
-      "integrity": "sha512-vrcjpVegYSwTSC8JfcE/qmmv1lsqDDhKvLqT8rMhW4DiogH8sVThJ1w5o2qOELXON2ArqfAxW8+DVmHsTPCUzQ==",
-      "dev": true,
-      "license": "GPL-2.0-or-later",
-      "engines": {
-        "node": ">=18.12.0",
-        "npm": ">=8.19.2"
-      },
-      "peerDependencies": {
-        "prettier": ">=3"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/@wordpress/warning": {
-      "version": "3.34.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.34.0.tgz",
-      "integrity": "sha512-WemuVXjaekzCDsWbDPj/RZSy44mIjPIy35DaoJgfLcgkXMH2GRBRSomhZMkWyGatD39vdXm0nqe95LsLDqrwCg==",
-      "dev": true,
-      "license": "GPL-2.0-or-later",
-      "engines": {
-        "node": ">=18.12.0",
-        "npm": ">=8.19.2"
-      }
-    },
     "node_modules/@wordpress/eslint-plugin/node_modules/cosmiconfig": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@@ -6297,38 +9683,22 @@
       }
     },
     "node_modules/@wordpress/eslint-plugin/node_modules/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@wordpress/eslint-plugin/node_modules/type-fest": {
-      "version": "0.20.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/@wordpress/eslint-plugin/node_modules/yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "version": "1.10.3",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+      "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
       "dev": true,
       "license": "ISC",
       "engines": {
@@ -6336,83 +9706,102 @@
       }
     },
     "node_modules/@wordpress/hooks": {
-      "version": "3.58.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.58.0.tgz",
-      "integrity": "sha512-9LB0ZHnZRQlORttux9t/xbAskF+dk2ujqzPGsVzc92mSKpQP3K2a5Wy74fUnInguB1vLUNHT6nrNdkVom5qX1Q==",
-      "dev": true,
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.50.0.tgz",
+      "integrity": "sha512-dV/AeP9iH1UydVWoUoCPuM8FUx2XvL1i65dhiwRHmZxK/zz8nR5Rq75WISpwFzRYfsyRThjl4LwUxhFAQnHrDw==",
       "license": "GPL-2.0-or-later",
-      "dependencies": {
-        "@babel/runtime": "^7.16.0"
-      },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/i18n": {
-      "version": "4.58.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.58.0.tgz",
-      "integrity": "sha512-VfvS3BWv/RDjRKD6PscIcvYfWKnGJcI/DEqyDgUMhxCM6NRwoL478CsUKTiGJIymeyRodNRfprdcF086DpGKYw==",
+      "version": "6.23.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.23.0.tgz",
+      "integrity": "sha512-avuywVdBpOeHm9cghFcNXCKi/VlJhbJ6KN6LydQOy/NqdRbqyRc59/XlKk1Ks+Vb+dWTGKr5gjiSzm9QJETGmA==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "^7.16.0",
-        "@wordpress/hooks": "^3.58.0",
+        "@tannin/sprintf": "^1.3.2",
+        "@wordpress/hooks": "^4.50.0",
         "gettext-parser": "^1.3.1",
         "memize": "^2.1.0",
-        "sprintf-js": "^1.1.1",
         "tannin": "^1.2.0"
       },
       "bin": {
         "pot-to-php": "tools/pot-to-php.js"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/icons": {
-      "version": "10.32.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.32.0.tgz",
-      "integrity": "sha512-1WvJdT361X1LnetYBpBWUjAVXZzl+pBdIwHbYRAp8ej47EI/igPmNxmq81nFd40s8fer/9qtipielcqSI6H2rA==",
+      "version": "15.1.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-15.1.0.tgz",
+      "integrity": "sha512-KPFEe24QJ5MTp6XMREKPPW1tHWjQau/vwYuZqkddSiJEhUBSrNaDHRqneCMC6WVhWCSmWNJcjUtdkFQpLb5W+A==",
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "7.25.7",
-        "@wordpress/element": "^6.32.0",
-        "@wordpress/primitives": "^4.32.0"
+        "@wordpress/element": "^8.2.0",
+        "@wordpress/primitives": "^4.50.0",
+        "change-case": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
+      "peerDependencies": {
+        "@types/react": "^18.3.27",
+        "react": "^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@wordpress/is-shallow-equal": {
+      "version": "5.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.50.0.tgz",
+      "integrity": "sha512-Cq1brW/OhGVNnCqSztHgmKQ1DsNUtEq4F1xIo1awBKVmXDWFggJRxzaWZHWC+sDZmBp7pu03jWqq2u/s+hazSQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
       "engines": {
         "node": ">=18.12.0",
         "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/jest-console": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-7.29.0.tgz",
-      "integrity": "sha512-/9PZJhyszdRX4mka7t1WzoooM+Q/DwC4jkNVtJxqci5lbL3Lrhy1cCJGCgMr1n/9w+zs7eLmExFBvV4v44iyNw==",
+      "version": "8.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-8.50.0.tgz",
+      "integrity": "sha512-iV6h5nWylCrL/4Wt7kXvq8YRwhryLgxThliaPLVvMfHb4CM6MJzapoIw5l8YIs1CDRlBU6N9j9oAxLBXvhrHqA==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "^7.16.0",
-        "jest-matcher-utils": "^29.6.2"
+        "jest-matcher-utils": "^29.6.2",
+        "jest-mock": "^29.6.2"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "jest": ">=29"
       }
     },
     "node_modules/@wordpress/jest-preset-default": {
-      "version": "11.29.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/jest-preset-default/-/jest-preset-default-11.29.0.tgz",
-      "integrity": "sha512-7LA0ZS5t0Thn7xrdwPL3hLgjB9LKloneGhMwnnDUTgJP330lyfdDfJ+O6Lnz3iL+bg68mkA3AzrT9Fs9f3WKww==",
+      "version": "12.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/jest-preset-default/-/jest-preset-default-12.50.0.tgz",
+      "integrity": "sha512-FA3pe0WMMg0l3l3/pumjX6rhKJXikO6BBRb/3ZeprD0xql5CTx30D7cKl0neluNjeZ2bd+1C3KYSvL81i4Y77g==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@wordpress/jest-console": "^7.29.0",
-        "babel-jest": "^29.6.2"
+        "@wordpress/jest-console": "^8.50.0",
+        "babel-jest": "^29.7.0"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "@babel/core": ">=7",
@@ -6420,70 +9809,82 @@
       }
     },
     "node_modules/@wordpress/keycodes": {
-      "version": "3.58.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-3.58.0.tgz",
-      "integrity": "sha512-Q/LRKpx8ndzuHlkxSQ2BD+NTYYKQPIneNNMng8hTAfyU7RFwXpqj06HpeOFGh4XIdPKCs/8hmucoLJRmmLmZJA==",
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.50.0.tgz",
+      "integrity": "sha512-BtyCkA7pk5pMMx+dVjT10OuhQwr9ZjYY1LLcXJcYJjhhFsP16be2BiytrBnKDZj/479/icvEYwlooawZCPrxYQ==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "^7.16.0",
-        "@wordpress/i18n": "^4.58.0"
+        "@wordpress/i18n": "^6.23.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      },
+      "peerDependencies": {
+        "@types/react": "^18.3.27"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
       }
     },
     "node_modules/@wordpress/npm-package-json-lint-config": {
-      "version": "4.43.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-4.43.0.tgz",
-      "integrity": "sha512-XSb7AdDC7yGTBVYeRM4oqmOygEB+/+tk7lobLIGDmlZJs+M3F/NUvQq0Vcas1pojq2fyPYTUwOlu81ga33fNwQ==",
+      "version": "5.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-5.50.0.tgz",
+      "integrity": "sha512-gI3vGO+R/kxUexTN2KfSy39ctg7QvOldZkEDZ90VVjK8Z6l46Uy5zvfMm94C6d5T7TgM9vcUkQghUd5ze0MXXA==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "npm-package-json-lint": ">=6.0.0"
       }
     },
     "node_modules/@wordpress/postcss-plugins-preset": {
-      "version": "4.42.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/postcss-plugins-preset/-/postcss-plugins-preset-4.42.0.tgz",
-      "integrity": "sha512-5xmKF7IUsqS5JcmJlHKHq7RaR6ZpaLj3n9c+X0X0/Oo7ZCIGp6WeDQngx13sH4NJoKXrZ9g4n1rbzhEKeo/Wtg==",
+      "version": "5.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/postcss-plugins-preset/-/postcss-plugins-preset-5.50.0.tgz",
+      "integrity": "sha512-uDpgDTGpHjjNHS+3E0hss0otRYeKVYulO2TA7jTpcXh9gikRCJYCQtFHh/eUCETrvSpzzfzIiKT7I048brWrcw==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@wordpress/base-styles": "^4.49.0",
-        "autoprefixer": "^10.2.5"
+        "@wordpress/base-styles": "^10.2.0",
+        "@wordpress/browserslist-config": "^6.50.0",
+        "autoprefixer": "^10.4.21",
+        "postcss-import": "^16.1.1"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "postcss": "^8.0.0"
       }
     },
     "node_modules/@wordpress/prettier-config": {
-      "version": "3.15.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-3.15.0.tgz",
-      "integrity": "sha512-exC2rkEioTt//AnzPRyaaFv8FNYIvamPDytNol5bKQ6Qh65QSdZZE9V+GtRCrIPL7/Bq6xba03XuRVxl9TjtJg==",
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-4.50.0.tgz",
+      "integrity": "sha512-Q48oFBORY0TiVWIebPOG0ynjUAq1Hi9gXKJ69xw2i/UwOwghLOXXFLdAQhS4bJXOw0cdV5xiBaXEQYU+82r5hA==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
         "prettier": ">=3"
       }
     },
     "node_modules/@wordpress/primitives": {
-      "version": "4.32.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.32.0.tgz",
-      "integrity": "sha512-pf46CU5qQaGOULlAMNQTi+Jkwf1vwfrGYmkRtuTP68/Y8yOI19v5JZg/Vwq5nCHOs/L750mX1wMp4WvGWoPhFg==",
+      "version": "4.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.50.0.tgz",
+      "integrity": "sha512-X/Vf1BUwPsxsyJq+z2UwjOHFoT4p1XI0VoxgN2JsbK0ZwbLPA9PQur1hGw9ujXqNZ4XG1U2rQ0aM+3yap7iC3g==",
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "7.25.7",
-        "@wordpress/element": "^6.32.0",
+        "@wordpress/element": "^8.2.0",
         "clsx": "^2.1.1"
       },
       "engines": {
@@ -6491,71 +9892,103 @@
         "npm": ">=8.19.2"
       },
       "peerDependencies": {
+        "@types/react": "^18.3.27",
         "react": "^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@wordpress/priority-queue": {
+      "version": "3.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.50.0.tgz",
+      "integrity": "sha512-OyuAplepKQpNYr8z+nGGGNXWK2v2u9Bqnawkg7V5YkvOKYj/iBJNCiUxiWXOBMeLF1ze36RDR5LEcNDmXRev2Q==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "requestidlecallback": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      }
+    },
+    "node_modules/@wordpress/private-apis": {
+      "version": "1.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.50.0.tgz",
+      "integrity": "sha512-bCVLL9YuWfnPNNqlhl4eTKPipjWbJ8MzAlasJ/SR+h979MyeMFjNPapxKqsPqnJOhDEVSdCTkRN/Wa+Xk8qFdw==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "engines": {
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/scripts": {
-      "version": "27.9.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-27.9.0.tgz",
-      "integrity": "sha512-ohiDHMnfTTBTi7qS7AVJZUi1dxwg0k3Aav1a8CzUoOE8YoT8tvMQ3W89H9XgqMgMTWUCdgTUBYLTJTivfVVbXQ==",
+      "version": "32.6.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-32.6.0.tgz",
+      "integrity": "sha512-nO5s2RYmOWiVjcG3/zp5NEejLu+J/dzujRIl633hd9yj8gBtQRKfZr/iAwyFQwW0Asn15bbwc1I0b4+lvnumOw==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/core": "^7.16.0",
+        "@babel/core": "^7.25.7",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
         "@svgr/webpack": "^8.0.1",
-        "@wordpress/babel-preset-default": "^7.42.0",
-        "@wordpress/browserslist-config": "^5.41.0",
-        "@wordpress/dependency-extraction-webpack-plugin": "^5.9.0",
-        "@wordpress/e2e-test-utils-playwright": "^0.26.0",
-        "@wordpress/eslint-plugin": "^18.1.0",
-        "@wordpress/jest-preset-default": "^11.29.0",
-        "@wordpress/npm-package-json-lint-config": "^4.43.0",
-        "@wordpress/postcss-plugins-preset": "^4.42.0",
-        "@wordpress/prettier-config": "^3.15.0",
-        "@wordpress/stylelint-config": "^21.41.0",
+        "@wordpress/babel-preset-default": "^8.50.0",
+        "@wordpress/browserslist-config": "^6.50.0",
+        "@wordpress/dependency-extraction-webpack-plugin": "^6.50.0",
+        "@wordpress/e2e-test-utils-playwright": "^1.50.0",
+        "@wordpress/eslint-plugin": "^25.6.0",
+        "@wordpress/jest-preset-default": "^12.50.0",
+        "@wordpress/npm-package-json-lint-config": "^5.50.0",
+        "@wordpress/postcss-plugins-preset": "^5.50.0",
+        "@wordpress/prettier-config": "^4.50.0",
+        "@wordpress/stylelint-config": "^23.42.0",
         "adm-zip": "^0.5.9",
-        "babel-jest": "^29.6.2",
-        "babel-loader": "^8.2.3",
+        "babel-jest": "^29.7.0",
+        "babel-loader": "^9.2.1",
         "browserslist": "^4.21.10",
-        "chalk": "^4.0.0",
+        "chalk": "^4.1.1",
         "check-node-version": "^4.1.0",
-        "clean-webpack-plugin": "^3.0.0",
         "copy-webpack-plugin": "^10.2.0",
-        "cross-spawn": "^5.1.0",
+        "cross-spawn": "^7.0.6",
         "css-loader": "^6.2.0",
         "cssnano": "^6.0.1",
         "cwd": "^0.10.0",
         "dir-glob": "^3.0.1",
-        "eslint": "^8.3.0",
+        "eslint": "^10.0.0",
         "expect-puppeteer": "^4.4.0",
         "fast-glob": "^3.2.7",
         "filenamify": "^4.2.0",
         "jest": "^29.6.2",
-        "jest-dev-server": "^9.0.1",
-        "jest-environment-jsdom": "^29.6.2",
+        "jest-dev-server": "^10.1.4",
+        "jest-environment-jsdom": "^30.2.0",
         "jest-environment-node": "^29.6.2",
+        "json2php": "^0.0.9",
         "markdownlint-cli": "^0.31.1",
         "merge-deep": "^3.0.3",
-        "mini-css-extract-plugin": "^2.5.1",
+        "mini-css-extract-plugin": "^2.9.2",
         "minimist": "^1.2.0",
         "npm-package-json-lint": "^6.4.0",
         "npm-packlist": "^3.0.0",
-        "postcss": "^8.4.5",
+        "postcss": "^8.4.38",
         "postcss-loader": "^6.2.1",
-        "prettier": "npm:wp-prettier@3.0.3",
-        "puppeteer-core": "^13.2.0",
+        "prettier": "npm:wp-prettier@^3.0.3",
+        "puppeteer-core": "^23.10.1",
         "react-refresh": "^0.14.0",
         "read-pkg-up": "^7.0.1",
         "resolve-bin": "^0.4.0",
-        "rtlcss-webpack-plugin": "^4.0.7",
-        "sass": "^1.35.2",
-        "sass-loader": "^12.1.0",
+        "rtlcss": "^4.3.0",
+        "sass": "^1.54.0",
+        "sass-loader": "^16.0.3",
+        "schema-utils": "^4.2.0",
         "source-map-loader": "^3.0.0",
-        "stylelint": "^14.2.0",
-        "terser-webpack-plugin": "^5.3.9",
+        "stylelint": "^16.8.2",
+        "terser-webpack-plugin": "^5.3.10",
         "url-loader": "^4.1.1",
-        "webpack": "^5.88.2",
+        "webpack": "^5.97.0",
         "webpack-bundle-analyzer": "^4.9.1",
         "webpack-cli": "^5.1.4",
         "webpack-dev-server": "^4.15.1"
@@ -6564,163 +9997,369 @@
         "wp-scripts": "bin/wp-scripts.js"
       },
       "engines": {
-        "node": ">=18",
-        "npm": ">=6.14.4"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
-        "@playwright/test": "^1.43.0",
+        "@playwright/test": "^1.58.2",
+        "@wordpress/env": ">=10.0.0",
         "react": "^18.0.0",
         "react-dom": "^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@wordpress/env": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/@wordpress/eslint-plugin": {
-      "version": "18.1.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-18.1.0.tgz",
-      "integrity": "sha512-5eGpXEwaZsKbEh9040nVr4ggmrpPmltP+Ie4iGruWvCme6ZIFYw70CyWEV8S102IkqjH/BaH6d+CWg8tN7sc/g==",
+    "node_modules/@wordpress/scripts/node_modules/@eslint/config-array": {
+      "version": "0.23.5",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
       "dev": true,
-      "license": "GPL-2.0-or-later",
+      "license": "Apache-2.0",
       "dependencies": {
-        "@babel/eslint-parser": "^7.16.0",
-        "@typescript-eslint/eslint-plugin": "^6.4.1",
-        "@typescript-eslint/parser": "^6.4.1",
-        "@wordpress/babel-preset-default": "^7.42.0",
-        "@wordpress/prettier-config": "^3.15.0",
-        "cosmiconfig": "^7.0.0",
-        "eslint-config-prettier": "^8.3.0",
-        "eslint-plugin-import": "^2.25.2",
-        "eslint-plugin-jest": "^27.2.3",
-        "eslint-plugin-jsdoc": "^46.4.6",
-        "eslint-plugin-jsx-a11y": "^6.5.1",
-        "eslint-plugin-playwright": "^0.15.3",
-        "eslint-plugin-prettier": "^5.0.0",
-        "eslint-plugin-react": "^7.27.0",
-        "eslint-plugin-react-hooks": "^4.3.0",
-        "globals": "^13.12.0",
-        "requireindex": "^1.2.0"
+        "@eslint/object-schema": "^3.0.5",
+        "debug": "^4.3.1",
+        "minimatch": "^10.2.4"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/@eslint/config-helpers": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+      "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^1.2.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/@eslint/object-schema": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/@eslint/plugin-kit": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+      "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^1.2.1",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/brace-expansion": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+      "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/cssnano": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
+      "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
+      "dev": true,
+      "dependencies": {
+        "cssnano-preset-default": "^6.1.2",
+        "lilconfig": "^3.1.1"
       },
       "engines": {
-        "node": ">=14",
-        "npm": ">=6.14.4"
+        "node": "^14 || ^16 || >=18.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/cssnano"
       },
       "peerDependencies": {
-        "@babel/core": ">=7",
-        "eslint": ">=8",
-        "prettier": ">=3",
-        "typescript": ">=4"
+        "postcss": "^8.4.31"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/cssnano-preset-default": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
+      "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
+      "dev": true,
+      "dependencies": {
+        "browserslist": "^4.23.0",
+        "css-declaration-sorter": "^7.2.0",
+        "cssnano-utils": "^4.0.2",
+        "postcss-calc": "^9.0.1",
+        "postcss-colormin": "^6.1.0",
+        "postcss-convert-values": "^6.1.0",
+        "postcss-discard-comments": "^6.0.2",
+        "postcss-discard-duplicates": "^6.0.3",
+        "postcss-discard-empty": "^6.0.3",
+        "postcss-discard-overridden": "^6.0.2",
+        "postcss-merge-longhand": "^6.0.5",
+        "postcss-merge-rules": "^6.1.1",
+        "postcss-minify-font-values": "^6.1.0",
+        "postcss-minify-gradients": "^6.0.3",
+        "postcss-minify-params": "^6.1.0",
+        "postcss-minify-selectors": "^6.0.4",
+        "postcss-normalize-charset": "^6.0.2",
+        "postcss-normalize-display-values": "^6.0.2",
+        "postcss-normalize-positions": "^6.0.2",
+        "postcss-normalize-repeat-style": "^6.0.2",
+        "postcss-normalize-string": "^6.0.2",
+        "postcss-normalize-timing-functions": "^6.0.2",
+        "postcss-normalize-unicode": "^6.1.0",
+        "postcss-normalize-url": "^6.0.2",
+        "postcss-normalize-whitespace": "^6.0.2",
+        "postcss-ordered-values": "^6.0.2",
+        "postcss-reduce-initial": "^6.1.0",
+        "postcss-reduce-transforms": "^6.0.2",
+        "postcss-svgo": "^6.0.3",
+        "postcss-unique-selectors": "^6.0.4"
+      },
+      "engines": {
+        "node": "^14 || ^16 || >=18.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.31"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/cssnano-utils": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
+      "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+      "dev": true,
+      "engines": {
+        "node": "^14 || ^16 || >=18.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.31"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/eslint": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
+      "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
+      "dev": true,
+      "license": "MIT",
+      "workspaces": [
+        "packages/*"
+      ],
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.2",
+        "@eslint/config-array": "^0.23.5",
+        "@eslint/config-helpers": "^0.6.0",
+        "@eslint/core": "^1.2.1",
+        "@eslint/plugin-kit": "^0.7.2",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.14.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^9.1.2",
+        "eslint-visitor-keys": "^5.0.1",
+        "espree": "^11.2.0",
+        "esquery": "^1.7.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "minimatch": "^10.2.4",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
       },
       "peerDependenciesMeta": {
-        "prettier": {
-          "optional": true
-        },
-        "typescript": {
+        "jiti": {
           "optional": true
         }
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/cosmiconfig": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
-      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+    "node_modules/@wordpress/scripts/node_modules/eslint-scope": {
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@types/esrecurse": "^4.3.1",
+        "@types/estree": "^1.0.8",
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/espree": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.16.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^5.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/filename-reserved-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
+      "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@wordpress/scripts/node_modules/filenamify": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
+      "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.2.1",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.10.0"
+        "filename-reserved-regex": "^2.0.0",
+        "strip-outer": "^1.0.1",
+        "trim-repeated": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       },
-      "engines": {
-        "node": ">=10"
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/cssnano": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz",
-      "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==",
+    "node_modules/@wordpress/scripts/node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "cssnano-preset-default": "^6.1.2",
-        "lilconfig": "^3.1.1"
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
       },
       "engines": {
-        "node": "^14 || ^16 || >=18.0"
+        "node": ">=10"
       },
       "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/cssnano"
-      },
-      "peerDependencies": {
-        "postcss": "^8.4.31"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/cssnano-preset-default": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz",
-      "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==",
+    "node_modules/@wordpress/scripts/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.23.0",
-        "css-declaration-sorter": "^7.2.0",
-        "cssnano-utils": "^4.0.2",
-        "postcss-calc": "^9.0.1",
-        "postcss-colormin": "^6.1.0",
-        "postcss-convert-values": "^6.1.0",
-        "postcss-discard-comments": "^6.0.2",
-        "postcss-discard-duplicates": "^6.0.3",
-        "postcss-discard-empty": "^6.0.3",
-        "postcss-discard-overridden": "^6.0.2",
-        "postcss-merge-longhand": "^6.0.5",
-        "postcss-merge-rules": "^6.1.1",
-        "postcss-minify-font-values": "^6.1.0",
-        "postcss-minify-gradients": "^6.0.3",
-        "postcss-minify-params": "^6.1.0",
-        "postcss-minify-selectors": "^6.0.4",
-        "postcss-normalize-charset": "^6.0.2",
-        "postcss-normalize-display-values": "^6.0.2",
-        "postcss-normalize-positions": "^6.0.2",
-        "postcss-normalize-repeat-style": "^6.0.2",
-        "postcss-normalize-string": "^6.0.2",
-        "postcss-normalize-timing-functions": "^6.0.2",
-        "postcss-normalize-unicode": "^6.1.0",
-        "postcss-normalize-url": "^6.0.2",
-        "postcss-normalize-whitespace": "^6.0.2",
-        "postcss-ordered-values": "^6.0.2",
-        "postcss-reduce-initial": "^6.1.0",
-        "postcss-reduce-transforms": "^6.0.2",
-        "postcss-svgo": "^6.0.3",
-        "postcss-unique-selectors": "^6.0.4"
+        "p-locate": "^5.0.0"
       },
       "engines": {
-        "node": "^14 || ^16 || >=18.0"
+        "node": ">=10"
       },
-      "peerDependencies": {
-        "postcss": "^8.4.31"
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/cssnano-utils": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
-      "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+    "node_modules/@wordpress/scripts/node_modules/minimatch": {
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "brace-expansion": "^5.0.5"
+      },
       "engines": {
-        "node": "^14 || ^16 || >=18.0"
+        "node": "18 || 20 || >=22"
       },
-      "peerDependencies": {
-        "postcss": "^8.4.31"
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+    "node_modules/@wordpress/scripts/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "type-fest": "^0.20.2"
+        "p-limit": "^3.0.2"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=10"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -7149,68 +10788,259 @@
         "postcss": "^8.4.31"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/type-fest": {
-      "version": "0.20.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+    "node_modules/@wordpress/style-runtime": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/style-runtime/-/style-runtime-0.5.0.tgz",
+      "integrity": "sha512-hrXvdDUJpOzT1KIomgtysysgbc5bkkwAuJyEUAXiOCgVBXBeMkZlgfN19W0PuNY51j53K7VQ42txfayxgVmfgA==",
       "dev": true,
-      "license": "(MIT OR CC0-1.0)",
+      "license": "GPL-2.0-or-later",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
       }
     },
-    "node_modules/@wordpress/scripts/node_modules/yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+    "node_modules/@wordpress/stylelint-config": {
+      "version": "23.42.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/stylelint-config/-/stylelint-config-23.42.0.tgz",
+      "integrity": "sha512-k9lNU4sK3fdipdhHI2d/Rzd/NnfznPmOZ60ok8O8QhfCMZw2Jd1M8ckYqaIrHTKU62ZrcsugdfJlHodsdrxEzA==",
       "dev": true,
-      "license": "ISC",
+      "license": "MIT",
+      "dependencies": {
+        "@stylistic/stylelint-plugin": "^3.0.1",
+        "@wordpress/theme": "^0.17.0",
+        "stylelint-config-recommended": "^14.0.1",
+        "stylelint-config-recommended-scss": "^14.1.0"
+      },
       "engines": {
-        "node": ">= 6"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      },
+      "peerDependencies": {
+        "stylelint": "^16.8.2",
+        "stylelint-scss": "^6.4.0"
       }
     },
-    "node_modules/@wordpress/stylelint-config": {
-      "version": "21.41.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/stylelint-config/-/stylelint-config-21.41.0.tgz",
-      "integrity": "sha512-2wxFu8ICeRGF3Lxz7H7o2SU1u6pTI4mjuog39DgtCNb+v+f6yhgREDuNQEeti3Svb0rjj63AJ7r2CqLZk+EQIQ==",
+    "node_modules/@wordpress/theme": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/theme/-/theme-0.17.0.tgz",
+      "integrity": "sha512-BU+PLBLxMBM4WJYrd1QEwy+IA64vUP+8IEEB9lLx2NfB+3/45vvuvXx9o3qPKRDHvtMtEaRArld4Kn05UIW9IA==",
       "dev": true,
-      "license": "MIT",
+      "license": "GPL-2.0-or-later",
       "dependencies": {
-        "stylelint-config-recommended": "^6.0.0",
-        "stylelint-config-recommended-scss": "^5.0.2"
+        "@wordpress/compose": "^8.2.0",
+        "@wordpress/deprecated": "^4.49.0",
+        "@wordpress/element": "^8.1.0",
+        "@wordpress/private-apis": "^1.49.0",
+        "@wordpress/style-runtime": "^0.5.0",
+        "colorjs.io": "^0.6.0",
+        "memize": "^2.1.0"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       },
       "peerDependencies": {
-        "stylelint": "^14.2"
+        "@types/react": "^18.3.27",
+        "esbuild": "^0.27.2",
+        "postcss": "^8.0.0",
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0",
+        "stylelint": "^16.8.2",
+        "vite": "^7.3.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "stylelint": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@wordpress/url": {
-      "version": "3.59.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-3.59.0.tgz",
-      "integrity": "sha512-GxvoMjYCav0w4CiX0i0h3qflrE/9rhLIZg5aPCQjbrBdwTxYR3Exfw0IJYcmVaTKXQOUU8fOxlDxULsbLmKe9w==",
+    "node_modules/@wordpress/undo-manager": {
+      "version": "1.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.50.0.tgz",
+      "integrity": "sha512-599I3GPXSMpmAE2jqzHteqypI7rdt5HPDAw8GrHMkDWR1T9gK2L3gObv8I1Hs4eSDM88wgAB0Ifnqpy7zPn5rw==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "dependencies": {
-        "@babel/runtime": "^7.16.0",
-        "remove-accents": "^0.5.0"
+        "@wordpress/is-shallow-equal": "^5.50.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
       }
     },
     "node_modules/@wordpress/warning": {
-      "version": "2.58.0",
-      "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-2.58.0.tgz",
-      "integrity": "sha512-9bZlORhyMY2nbWozeyC5kqJsFzEPP4DCLhGmjtbv+YWGHttUrxUZEfrKdqO+rUODA8rP5zeIly1nCQOUnkw4Lg==",
+      "version": "3.50.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.50.0.tgz",
+      "integrity": "sha512-6X3ioKOGfmRuZFngmG2QZZW9CBVMTBr5FXqs6Q3li5ryhnAqn3u/ocqsx556YnB5dYdVxK14TiH9o7DDElt8gw==",
       "dev": true,
       "license": "GPL-2.0-or-later",
       "engines": {
-        "node": ">=12"
+        "node": ">=18.12.0",
+        "npm": ">=8.19.2"
+      }
+    },
+    "node_modules/@wp-playground/blueprints": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-3.1.41.tgz",
+      "integrity": "sha512-UhveYQs/W+IXxmOP7jBLN8kE/xqI/vTj1OhLrwPMaL9mcmQiow6v7O4ysFiNehDuLQGTAecNmr1nOdKUAQx03g==",
+      "dev": true,
+      "dependencies": {
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/progress": "3.1.41",
+        "@php-wasm/stream-compression": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "@php-wasm/web-service-worker": "3.1.41",
+        "@wp-playground/common": "3.1.41",
+        "@wp-playground/storage": "3.1.41",
+        "@wp-playground/wordpress": "3.1.41",
+        "ajv": "8.18.0"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@wp-playground/blueprints/node_modules/ajv": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/@wp-playground/blueprints/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@wp-playground/cli": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/cli/-/cli-3.1.41.tgz",
+      "integrity": "sha512-7ceotUA94ZtRE8mA9k86QOKAU3KTQk3I8YPZgbn3x85FD1e9fRtr4LAK9eeX9NkvytEUm/xIqBaQ3IvdRYYFDw==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/cli-util": "3.1.41",
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/node": "3.1.41",
+        "@php-wasm/progress": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "@php-wasm/xdebug-bridge": "3.1.41",
+        "@wp-playground/blueprints": "3.1.41",
+        "@wp-playground/common": "3.1.41",
+        "@wp-playground/storage": "3.1.41",
+        "@wp-playground/tools": "3.1.41",
+        "@wp-playground/wordpress": "3.1.41",
+        "express": "4.22.2",
+        "fs-extra": "11.1.1",
+        "tmp-promise": "3.0.3",
+        "wasm-feature-detect": "1.8.0",
+        "yargs": "17.7.2"
+      },
+      "bin": {
+        "wp-playground-cli": "wp-playground.js"
+      }
+    },
+    "node_modules/@wp-playground/cli/node_modules/fs-extra": {
+      "version": "11.1.1",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
+      "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/@wp-playground/common": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/common/-/common-3.1.41.tgz",
+      "integrity": "sha512-BhsyR7feAFCiUoNgZEA3mHAPjecK+l/S/bnNl6VhhT+NXLg/sWxzmIG/EvxCHHBVF66xnUlI25NjsLw0pokC/Q==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@wp-playground/storage": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/storage/-/storage-3.1.41.tgz",
+      "integrity": "sha512-oBoElqKZj+6ZSGzyfn6kJzD5B9SwNhKgAoZO5gmO+gC9oEZg6KZ41dJ1qD6duuTL9Q/qGuh3aBT3A6P5AtKDqQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/stream-compression": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "@zip.js/zip.js": "2.7.57",
+        "isomorphic-git": "1.37.6",
+        "octokit": "3.1.2",
+        "pako": "^1.0.10"
+      }
+    },
+    "node_modules/@wp-playground/tools": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/tools/-/tools-3.1.41.tgz",
+      "integrity": "sha512-xptMuuQF+t+EPc6uxg9yw/+jI+tKSroYrfJ4Cb5UGCT+EAXqaselvDRIYO/w3N67yAyadthES8uyHscS6ZpzyQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@wp-playground/blueprints": "3.1.41"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
+      }
+    },
+    "node_modules/@wp-playground/wordpress": {
+      "version": "3.1.41",
+      "resolved": "https://registry.npmjs.org/@wp-playground/wordpress/-/wordpress-3.1.41.tgz",
+      "integrity": "sha512-bKK318um+vxuMLbQqeIc8KiOeV3PRgEm3i8YTr/BCOpRhbDkRZV5yT/0i+ccimrRd2JJ6KyURyw6N6r81wOEXQ==",
+      "dev": true,
+      "license": "GPL-2.0-or-later",
+      "dependencies": {
+        "@php-wasm/logger": "3.1.41",
+        "@php-wasm/universal": "3.1.41",
+        "@php-wasm/util": "3.1.41",
+        "@wp-playground/common": "3.1.41"
+      },
+      "engines": {
+        "node": ">=20.10.0",
+        "npm": ">=10.2.3"
       }
     },
     "node_modules/@xtuc/ieee754": {
@@ -7227,6 +11057,18 @@
       "dev": true,
       "license": "Apache-2.0"
     },
+    "node_modules/@zip.js/zip.js": {
+      "version": "2.7.57",
+      "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz",
+      "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "bun": ">=0.7.0",
+        "deno": ">=1.0.0",
+        "node": ">=16.5.0"
+      }
+    },
     "node_modules/abab": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -7242,6 +11084,19 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
     "node_modules/accepts": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -7257,9 +11112,9 @@
       }
     },
     "node_modules/acorn": {
-      "version": "8.14.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
-      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+      "version": "8.17.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+      "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
       "dev": true,
       "license": "MIT",
       "bin": {
@@ -7269,15 +11124,14 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/acorn-globals": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
-      "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+    "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",
-      "dependencies": {
-        "acorn": "^8.1.0",
-        "acorn-walk": "^8.0.2"
+      "peerDependencies": {
+        "acorn": "^8"
       }
     },
     "node_modules/acorn-jsx": {
@@ -7314,16 +11168,13 @@
       }
     },
     "node_modules/agent-base": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
-      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "debug": "4"
-      },
       "engines": {
-        "node": ">= 6.0.0"
+        "node": ">= 14"
       }
     },
     "node_modules/aggregate-error": {
@@ -7341,9 +11192,9 @@
       }
     },
     "node_modules/ajv": {
-      "version": "6.12.6",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "version": "6.15.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+      "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -7497,10 +11348,10 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/ansicolors": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
-      "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
+    "node_modules/any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
       "dev": true,
       "license": "MIT"
     },
@@ -7518,6 +11369,19 @@
         "node": ">= 8"
       }
     },
+    "node_modules/anynum": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+      "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/are-docs-informative": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
@@ -7614,18 +11478,20 @@
       "license": "MIT"
     },
     "node_modules/array-includes": {
-      "version": "3.1.8",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
-      "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+      "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "is-string": "^1.0.7"
+        "es-abstract": "^1.24.0",
+        "es-object-atoms": "^1.1.1",
+        "get-intrinsic": "^1.3.0",
+        "is-string": "^1.1.1",
+        "math-intrinsics": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -7644,24 +11510,14 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/array-union": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
-      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/array-uniq": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-      "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=8"
       }
     },
     "node_modules/array.prototype.findlast": {
@@ -7841,6 +11697,13 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/async-lock": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
+      "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -7848,10 +11711,21 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/atomically": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz",
+      "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "stubborn-fs": "^2.0.0",
+        "when-exit": "^2.1.4"
+      }
+    },
     "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.5.2",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz",
+      "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==",
       "dev": true,
       "funding": [
         {
@@ -7869,11 +11743,10 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.23.0",
-        "caniuse-lite": "^1.0.30001599",
-        "fraction.js": "^4.3.7",
-        "normalize-range": "^0.1.2",
-        "picocolors": "^1.0.0",
+        "browserslist": "^4.28.4",
+        "caniuse-lite": "^1.0.30001799",
+        "fraction.js": "^5.3.4",
+        "picocolors": "^1.1.1",
         "postcss-value-parser": "^4.2.0"
       },
       "bin": {
@@ -7903,9 +11776,9 @@
       }
     },
     "node_modules/axe-core": {
-      "version": "4.10.3",
-      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
-      "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz",
+      "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==",
       "dev": true,
       "license": "MPL-2.0",
       "engines": {
@@ -7913,15 +11786,53 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
-      "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
+      "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.16.0",
+        "form-data": "^4.0.5",
+        "https-proxy-agent": "^5.0.1",
+        "proxy-from-env": "^2.1.0"
+      }
+    },
+    "node_modules/axios/node_modules/agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/axios/node_modules/https-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "follow-redirects": "^1.15.6",
-        "form-data": "^4.0.0",
-        "proxy-from-env": "^1.1.0"
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/axios/node_modules/proxy-from-env": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+      "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
       }
     },
     "node_modules/axobject-query": {
@@ -7934,13 +11845,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/b4a": {
-      "version": "1.6.7",
-      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
-      "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
     "node_modules/babel-jest": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -7964,42 +11868,21 @@
       }
     },
     "node_modules/babel-loader": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz",
-      "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==",
+      "version": "9.2.1",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
+      "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "find-cache-dir": "^3.3.1",
-        "loader-utils": "^2.0.0",
-        "make-dir": "^3.1.0",
-        "schema-utils": "^2.6.5"
+        "find-cache-dir": "^4.0.0",
+        "schema-utils": "^4.0.0"
       },
       "engines": {
-        "node": ">= 8.9"
+        "node": ">= 14.15.0"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0",
-        "webpack": ">=2"
-      }
-    },
-    "node_modules/babel-loader/node_modules/schema-utils": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
-      "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.5",
-        "ajv": "^6.12.4",
-        "ajv-keywords": "^3.5.2"
-      },
-      "engines": {
-        "node": ">= 8.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
+        "@babel/core": "^7.12.0",
+        "webpack": ">=5"
       }
     },
     "node_modules/babel-plugin-istanbul": {
@@ -8036,14 +11919,14 @@
       }
     },
     "node_modules/babel-plugin-polyfill-corejs2": {
-      "version": "0.4.11",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz",
-      "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==",
+      "version": "0.4.17",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
+      "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/compat-data": "^7.22.6",
-        "@babel/helper-define-polyfill-provider": "^0.6.2",
+        "@babel/compat-data": "^7.28.6",
+        "@babel/helper-define-polyfill-provider": "^0.6.8",
         "semver": "^6.3.1"
       },
       "peerDependencies": {
@@ -8065,40 +11948,43 @@
       }
     },
     "node_modules/babel-plugin-polyfill-regenerator": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz",
-      "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==",
+      "version": "0.6.8",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
+      "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.2"
+        "@babel/helper-define-polyfill-provider": "^0.6.8"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
     "node_modules/babel-preset-current-node-syntax": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
-      "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
+      "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/plugin-syntax-async-generators": "^7.8.4",
         "@babel/plugin-syntax-bigint": "^7.8.3",
-        "@babel/plugin-syntax-class-properties": "^7.8.3",
-        "@babel/plugin-syntax-import-meta": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.12.13",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5",
+        "@babel/plugin-syntax-import-attributes": "^7.24.7",
+        "@babel/plugin-syntax-import-meta": "^7.10.4",
         "@babel/plugin-syntax-json-strings": "^7.8.3",
-        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
         "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
-        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
         "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
         "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
         "@babel/plugin-syntax-optional-chaining": "^7.8.3",
-        "@babel/plugin-syntax-top-level-await": "^7.8.3"
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+        "@babel/plugin-syntax-top-level-await": "^7.14.5"
       },
       "peerDependencies": {
-        "@babel/core": "^7.0.0"
+        "@babel/core": "^7.0.0 || ^8.0.0-0"
       }
     },
     "node_modules/babel-preset-jest": {
@@ -8118,33 +12004,6 @@
         "@babel/core": "^7.0.0"
       }
     },
-    "node_modules/babel-runtime": {
-      "version": "6.25.0",
-      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz",
-      "integrity": "sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.10.0"
-      }
-    },
-    "node_modules/babel-runtime/node_modules/core-js": {
-      "version": "2.6.12",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
-      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
-      "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
-      "dev": true,
-      "hasInstallScript": true,
-      "license": "MIT"
-    },
-    "node_modules/babel-runtime/node_modules/regenerator-runtime": {
-      "version": "0.10.5",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
-      "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/bail": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
@@ -8164,12 +12023,117 @@
       "license": "MIT"
     },
     "node_modules/bare-events": {
-      "version": "2.5.4",
-      "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
-      "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
+      "version": "2.9.1",
+      "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
+      "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
       "dev": true,
       "license": "Apache-2.0",
-      "optional": true
+      "peerDependencies": {
+        "bare-abort-controller": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-abort-controller": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/bare-fs": {
+      "version": "4.7.2",
+      "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz",
+      "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bare-events": "^2.5.4",
+        "bare-path": "^3.0.0",
+        "bare-stream": "^2.6.4",
+        "bare-url": "^2.2.2",
+        "fast-fifo": "^1.3.2"
+      },
+      "engines": {
+        "bare": ">=1.16.0"
+      },
+      "peerDependencies": {
+        "bare-buffer": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-buffer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/bare-os": {
+      "version": "3.9.1",
+      "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz",
+      "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "bare": ">=1.14.0"
+      }
+    },
+    "node_modules/bare-path": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz",
+      "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bare-os": "^3.0.1"
+      }
+    },
+    "node_modules/bare-stream": {
+      "version": "2.13.3",
+      "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
+      "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "b4a": "^1.8.1",
+        "streamx": "^2.25.0",
+        "teex": "^1.0.1"
+      },
+      "peerDependencies": {
+        "bare-abort-controller": "*",
+        "bare-buffer": "*",
+        "bare-events": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-abort-controller": {
+          "optional": true
+        },
+        "bare-buffer": {
+          "optional": true
+        },
+        "bare-events": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/bare-stream/node_modules/b4a": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+      "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "react-native-b4a": "*"
+      },
+      "peerDependenciesMeta": {
+        "react-native-b4a": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/bare-url": {
+      "version": "2.4.5",
+      "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz",
+      "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bare-path": "^3.0.0"
+      }
     },
     "node_modules/base64-js": {
       "version": "1.5.1",
@@ -8193,19 +12157,22 @@
       "license": "MIT"
     },
     "node_modules/baseline-browser-mapping": {
-      "version": "2.8.10",
-      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz",
-      "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==",
+      "version": "2.10.38",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+      "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
       "dev": true,
       "license": "Apache-2.0",
       "bin": {
-        "baseline-browser-mapping": "dist/cli.js"
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
       }
     },
     "node_modules/basic-ftp": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
-      "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz",
+      "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -8220,9 +12187,9 @@
       "license": "MIT"
     },
     "node_modules/before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
+      "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
       "dev": true,
       "license": "Apache-2.0"
     },
@@ -8249,52 +12216,25 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/bl": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
-      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "buffer": "^5.5.0",
-        "inherits": "^2.0.4",
-        "readable-stream": "^3.4.0"
-      }
-    },
-    "node_modules/bl/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/body-parser": {
-      "version": "1.20.3",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
-      "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+      "version": "1.20.5",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+      "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "bytes": "3.1.2",
+        "bytes": "~3.1.2",
         "content-type": "~1.0.5",
         "debug": "2.6.9",
         "depd": "2.0.0",
-        "destroy": "1.2.0",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "on-finished": "2.4.1",
-        "qs": "6.13.0",
-        "raw-body": "2.5.2",
+        "destroy": "~1.2.0",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "on-finished": "~2.4.1",
+        "qs": "~6.15.1",
+        "raw-body": "~2.5.3",
         "type-is": "~1.6.18",
-        "unpipe": "1.0.0"
+        "unpipe": "~1.0.0"
       },
       "engines": {
         "node": ">= 0.8",
@@ -8321,6 +12261,27 @@
         "ms": "2.0.0"
       }
     },
+    "node_modules/body-parser/node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
     "node_modules/body-parser/node_modules/iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -8341,6 +12302,16 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/body-parser/node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/bonjour-service": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz",
@@ -8390,9 +12361,9 @@
       }
     },
     "node_modules/browserslist": {
-      "version": "4.26.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz",
-      "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==",
+      "version": "4.28.4",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
+      "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
       "dev": true,
       "funding": [
         {
@@ -8410,11 +12381,11 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "baseline-browser-mapping": "^2.8.9",
-        "caniuse-lite": "^1.0.30001746",
-        "electron-to-chromium": "^1.5.227",
-        "node-releases": "^2.0.21",
-        "update-browserslist-db": "^1.1.3"
+        "baseline-browser-mapping": "^2.10.38",
+        "caniuse-lite": "^1.0.30001799",
+        "electron-to-chromium": "^1.5.376",
+        "node-releases": "^2.0.48",
+        "update-browserslist-db": "^1.2.3"
       },
       "bin": {
         "browserslist": "cli.js"
@@ -8433,6 +12404,13 @@
         "node-int64": "^0.4.0"
       }
     },
+    "node_modules/btoa-lite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
+      "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -8468,6 +12446,13 @@
         "node": "*"
       }
     },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
     "node_modules/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -8475,19 +12460,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/builtin-modules": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
-      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/bytes": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -8498,6 +12470,20 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/cacheable": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz",
+      "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@cacheable/memory": "^2.2.0",
+        "@cacheable/utils": "^2.5.0",
+        "hookified": "^1.15.0",
+        "keyv": "^5.6.0",
+        "qified": "^0.10.1"
+      }
+    },
     "node_modules/cacheable-lookup": {
       "version": "5.0.4",
       "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
@@ -8543,16 +12529,26 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/cacheable/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+      "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@keyv/serialize": "^1.1.1"
+      }
+    },
     "node_modules/call-bind": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
-      "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
+      "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind-apply-helpers": "^1.0.0",
-        "es-define-property": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "get-intrinsic": "^1.3.0",
         "set-function-length": "^1.2.2"
       },
       "engines": {
@@ -8667,9 +12663,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001746",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz",
-      "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==",
+      "version": "1.0.30001799",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+      "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
       "dev": true,
       "funding": [
         {
@@ -8698,20 +12694,6 @@
         "upper-case-first": "^2.0.2"
       }
     },
-    "node_modules/cardinal": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz",
-      "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansicolors": "~0.3.2",
-        "redeyed": "~2.1.0"
-      },
-      "bin": {
-        "cdl": "bin/cdl.js"
-      }
-    },
     "node_modules/ccount": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz",
@@ -8811,9 +12793,9 @@
       "license": "MIT"
     },
     "node_modules/chart.js": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
-      "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
+      "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
       "license": "MIT",
       "dependencies": {
         "@kurkle/color": "^0.3.0"
@@ -8895,27 +12877,20 @@
         "node": ">= 6"
       }
     },
-    "node_modules/chownr": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-      "dev": true,
-      "license": "ISC"
-    },
     "node_modules/chrome-launcher": {
-      "version": "0.15.2",
-      "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz",
-      "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.1.tgz",
+      "integrity": "sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@types/node": "*",
         "escape-string-regexp": "^4.0.0",
         "is-wsl": "^2.2.0",
-        "lighthouse-logger": "^1.0.0"
+        "lighthouse-logger": "^2.0.1"
       },
       "bin": {
-        "print-chrome-path": "bin/print-chrome-path.js"
+        "print-chrome-path": "bin/print-chrome-path.cjs"
       },
       "engines": {
         "node": ">=12.13.0"
@@ -8931,6 +12906,20 @@
         "node": ">=6.0"
       }
     },
+    "node_modules/chromium-bidi": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz",
+      "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "mitt": "3.0.1",
+        "zod": "3.23.8"
+      },
+      "peerDependencies": {
+        "devtools-protocol": "*"
+      }
+    },
     "node_modules/ci-info": {
       "version": "3.9.0",
       "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -8948,17 +12937,18 @@
       }
     },
     "node_modules/cjs-module-lexer": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
-      "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
+      "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/classnames": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
-      "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
-      "license": "MIT"
+    "node_modules/clean-git-ref": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz",
+      "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==",
+      "dev": true,
+      "license": "Apache-2.0"
     },
     "node_modules/clean-stack": {
       "version": "2.2.0",
@@ -8970,34 +12960,87 @@
         "node": ">=6"
       }
     },
-    "node_modules/clean-webpack-plugin": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
-      "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==",
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@types/webpack": "^4.4.31",
-        "del": "^4.1.1"
+        "restore-cursor": "^3.1.0"
       },
       "engines": {
-        "node": ">=8.9.0"
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-highlight": {
+      "version": "2.1.11",
+      "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+      "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "highlight.js": "^10.7.1",
+        "mz": "^2.4.0",
+        "parse5": "^5.1.1",
+        "parse5-htmlparser2-tree-adapter": "^6.0.0",
+        "yargs": "^16.0.0"
       },
-      "peerDependencies": {
-        "webpack": "*"
+      "bin": {
+        "highlight": "bin/highlight"
+      },
+      "engines": {
+        "node": ">=8.0.0",
+        "npm": ">=5.0.0"
       }
     },
-    "node_modules/cli-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
-      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+    "node_modules/cli-highlight/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/cli-highlight/node_modules/parse5": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cli-highlight/node_modules/yargs": {
+      "version": "16.2.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+      "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "restore-cursor": "^3.1.0"
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=10"
+      }
+    },
+    "node_modules/cli-highlight/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
       }
     },
     "node_modules/cli-spinners": {
@@ -9115,9 +13158,9 @@
       }
     },
     "node_modules/collect-v8-coverage": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
-      "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
+      "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
       "dev": true,
       "license": "MIT"
     },
@@ -9155,6 +13198,17 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/colorjs.io": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.6.1.tgz",
+      "integrity": "sha512-8lyR2wHzuIykCpqHKgluGsqQi5iDm3/a2IgP2GBZrasn2sBRkE4NOGsglZxWLs/jZQoNkmA/KM/8NV16rLUdBg==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/color"
+      }
+    },
     "node_modules/colors": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
@@ -9179,13 +13233,13 @@
       }
     },
     "node_modules/commander": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
-      "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">= 6"
+        "node": ">=18"
       }
     },
     "node_modules/comment-parser": {
@@ -9198,12 +13252,12 @@
         "node": ">= 12.0.0"
       }
     },
-    "node_modules/commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+    "node_modules/common-path-prefix": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
+      "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
       "dev": true,
-      "license": "MIT"
+      "license": "ISC"
     },
     "node_modules/compare-func": {
       "version": "1.3.4",
@@ -9279,22 +13333,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "engines": [
-        "node >= 0.8"
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      }
-    },
     "node_modules/config-chain": {
       "version": "1.1.13",
       "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
@@ -9307,57 +13345,51 @@
       }
     },
     "node_modules/configstore": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
-      "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz",
+      "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
-        "dot-prop": "^5.2.0",
-        "graceful-fs": "^4.1.2",
-        "make-dir": "^3.0.0",
-        "unique-string": "^2.0.0",
-        "write-file-atomic": "^3.0.0",
-        "xdg-basedir": "^4.0.0"
+        "atomically": "^2.0.3",
+        "dot-prop": "^9.0.0",
+        "graceful-fs": "^4.2.11",
+        "xdg-basedir": "^5.1.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/configstore/node_modules/dot-prop": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
-      "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
+      "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "is-obj": "^2.0.0"
+        "type-fest": "^4.18.2"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/configstore/node_modules/is-obj": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
-      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+    "node_modules/configstore/node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
       "dev": true,
-      "license": "MIT",
+      "license": "(MIT OR CC0-1.0)",
       "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/configstore/node_modules/write-file-atomic": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
-      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "imurmurhash": "^0.1.4",
-        "is-typedarray": "^1.0.0",
-        "signal-exit": "^3.0.2",
-        "typedarray-to-buffer": "^3.1.5"
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/connect-history-api-fallback": {
@@ -9405,17 +13437,16 @@
       }
     },
     "node_modules/conventional-changelog-angular": {
-      "version": "5.0.13",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz",
-      "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==",
+      "version": "8.3.1",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz",
+      "integrity": "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "compare-func": "^2.0.0",
-        "q": "^1.5.1"
+        "compare-func": "^2.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       }
     },
     "node_modules/conventional-changelog-angular/node_modules/compare-func": {
@@ -9467,172 +13498,104 @@
       }
     },
     "node_modules/conventional-changelog-writer": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz",
-      "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.4.0.tgz",
+      "integrity": "sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "conventional-commits-filter": "^2.0.7",
-        "dateformat": "^3.0.0",
+        "@simple-libs/stream-utils": "^1.2.0",
+        "conventional-commits-filter": "^5.0.0",
         "handlebars": "^4.7.7",
-        "json-stringify-safe": "^5.0.1",
-        "lodash": "^4.17.15",
-        "meow": "^8.0.0",
-        "semver": "^6.0.0",
-        "split": "^1.0.0",
-        "through2": "^4.0.0"
+        "meow": "^13.0.0",
+        "semver": "^7.5.2"
       },
       "bin": {
-        "conventional-changelog-writer": "cli.js"
+        "conventional-changelog-writer": "dist/cli/index.js"
       },
       "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/conventional-changelog-writer/node_modules/dateformat": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
-      "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "*"
+        "node": ">=18"
       }
     },
     "node_modules/conventional-changelog-writer/node_modules/meow": {
-      "version": "8.1.2",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
-      "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==",
+      "version": "13.2.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+      "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "@types/minimist": "^1.2.0",
-        "camelcase-keys": "^6.2.2",
-        "decamelize-keys": "^1.1.0",
-        "hard-rejection": "^2.1.0",
-        "minimist-options": "4.1.0",
-        "normalize-package-data": "^3.0.0",
-        "read-pkg-up": "^7.0.1",
-        "redent": "^3.0.0",
-        "trim-newlines": "^3.0.0",
-        "type-fest": "^0.18.0",
-        "yargs-parser": "^20.2.3"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/conventional-changelog-writer/node_modules/type-fest": {
-      "version": "0.18.1",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
-      "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/conventional-changelog-writer/node_modules/yargs-parser": {
-      "version": "20.2.9",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+    "node_modules/conventional-changelog-writer/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
       "engines": {
         "node": ">=10"
       }
     },
     "node_modules/conventional-commits-filter": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz",
-      "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz",
+      "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "lodash.ismatch": "^4.4.0",
-        "modify-values": "^1.0.0"
-      },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       }
     },
     "node_modules/conventional-commits-parser": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz",
-      "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz",
+      "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "is-text-path": "^1.0.1",
-        "JSONStream": "^1.0.4",
-        "lodash": "^4.17.15",
-        "meow": "^8.0.0",
-        "split2": "^3.0.0",
-        "through2": "^4.0.0"
+        "@simple-libs/stream-utils": "^1.2.0",
+        "meow": "^13.0.0"
       },
       "bin": {
-        "conventional-commits-parser": "cli.js"
+        "conventional-commits-parser": "dist/cli/index.js"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       }
     },
     "node_modules/conventional-commits-parser/node_modules/meow": {
-      "version": "8.1.2",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
-      "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==",
+      "version": "13.2.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+      "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "@types/minimist": "^1.2.0",
-        "camelcase-keys": "^6.2.2",
-        "decamelize-keys": "^1.1.0",
-        "hard-rejection": "^2.1.0",
-        "minimist-options": "4.1.0",
-        "normalize-package-data": "^3.0.0",
-        "read-pkg-up": "^7.0.1",
-        "redent": "^3.0.0",
-        "trim-newlines": "^3.0.0",
-        "type-fest": "^0.18.0",
-        "yargs-parser": "^20.2.3"
-      },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/conventional-commits-parser/node_modules/type-fest": {
-      "version": "0.18.1",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
-      "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
+    "node_modules/convert-hrtime": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz",
+      "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==",
       "dev": true,
-      "license": "(MIT OR CC0-1.0)",
+      "license": "MIT",
       "engines": {
-        "node": ">=10"
+        "node": ">=12"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/conventional-commits-parser/node_modules/yargs-parser": {
-      "version": "20.2.9",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/convert-source-map": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -9640,16 +13603,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/cookie": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/cookie-signature": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -9737,9 +13690,9 @@
       }
     },
     "node_modules/core-js": {
-      "version": "3.42.0",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.42.0.tgz",
-      "integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==",
+      "version": "3.49.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
+      "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
       "dev": true,
       "hasInstallScript": true,
       "license": "MIT",
@@ -9749,13 +13702,13 @@
       }
     },
     "node_modules/core-js-compat": {
-      "version": "3.42.0",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz",
-      "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==",
+      "version": "3.49.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz",
+      "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.24.4"
+        "browserslist": "^4.28.1"
       },
       "funding": {
         "type": "opencollective",
@@ -9828,6 +13781,19 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/create-jest": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
@@ -9847,63 +13813,96 @@
         "create-jest": "bin/create-jest.js"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
       }
     },
-    "node_modules/cross-fetch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
-      "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+    "node_modules/cross-spawn/node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "node-fetch": "2.6.7"
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "node_modules/cross-spawn": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-      "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
+    "node_modules/cross-spawn/node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "lru-cache": "^4.0.1",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "node_modules/cross-spawn/node_modules/lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+    "node_modules/cross-spawn/node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
       }
     },
-    "node_modules/cross-spawn/node_modules/yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
-      "dev": true,
-      "license": "ISC"
-    },
     "node_modules/crypto-random-string": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
-      "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+      "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "type-fest": "^1.0.1"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/crypto-random-string/node_modules/type-fest": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+      "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/csp_evaluator": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.1.tgz",
-      "integrity": "sha512-N3ASg0C4kNPUaNxt1XAvzHIVuzdtr8KLgfk1O8WDyimp1GisPAHESupArO2ieHk9QWbrJ/WkQODyh21Ps/xhxw==",
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.5.tgz",
+      "integrity": "sha512-EL/iN9etCTzw/fBnp0/uj0f5BOOGvZut2mzsiiBZ/FdT6gFQCKRO/tmcKOxn5drWZ2Ndm/xBb1SI4zwWbGtmIw==",
       "dev": true,
       "license": "Apache-2.0"
     },
@@ -9920,13 +13919,13 @@
       }
     },
     "node_modules/css-functions-list": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz",
-      "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==",
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz",
+      "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=12 || >=16"
+        "node": ">=12"
       }
     },
     "node_modules/css-loader": {
@@ -10036,82 +14035,81 @@
       }
     },
     "node_modules/cssnano": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz",
-      "integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==",
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-8.0.2.tgz",
+      "integrity": "sha512-K+a76gA1v0/CsYgcsE95HGGyIuPKxpQSetwSwz4nHEM8fFXqSkzq2JzEXFL8v5+CCjxzVVVhPcTK3Oo8SaF/xA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cssnano-preset-default": "^7.0.9",
+        "cssnano-preset-default": "^8.0.2",
         "lilconfig": "^3.1.3"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/cssnano"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/cssnano-preset-default": {
-      "version": "7.0.9",
-      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.9.tgz",
-      "integrity": "sha512-tCD6AAFgYBOVpMBX41KjbvRh9c2uUjLXRyV7KHSIrwHiq5Z9o0TFfUCoM3TwVrRsRteN3sVXGNvjVNxYzkpTsA==",
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-8.0.2.tgz",
+      "integrity": "sha512-+jQAqIKCqMmBjZs7741XkilU93ITZ/EW8gjAkMmujdCzfDkfjrDBv2VqkSu29Fzeig/0rZ3S9IAwfPLlmXEUfQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "css-declaration-sorter": "^7.2.0",
-        "cssnano-utils": "^5.0.1",
+        "browserslist": "^4.28.2",
+        "cssnano-utils": "^6.0.1",
         "postcss-calc": "^10.1.1",
-        "postcss-colormin": "^7.0.4",
-        "postcss-convert-values": "^7.0.7",
-        "postcss-discard-comments": "^7.0.4",
-        "postcss-discard-duplicates": "^7.0.2",
-        "postcss-discard-empty": "^7.0.1",
-        "postcss-discard-overridden": "^7.0.1",
-        "postcss-merge-longhand": "^7.0.5",
-        "postcss-merge-rules": "^7.0.6",
-        "postcss-minify-font-values": "^7.0.1",
-        "postcss-minify-gradients": "^7.0.1",
-        "postcss-minify-params": "^7.0.4",
-        "postcss-minify-selectors": "^7.0.5",
-        "postcss-normalize-charset": "^7.0.1",
-        "postcss-normalize-display-values": "^7.0.1",
-        "postcss-normalize-positions": "^7.0.1",
-        "postcss-normalize-repeat-style": "^7.0.1",
-        "postcss-normalize-string": "^7.0.1",
-        "postcss-normalize-timing-functions": "^7.0.1",
-        "postcss-normalize-unicode": "^7.0.4",
-        "postcss-normalize-url": "^7.0.1",
-        "postcss-normalize-whitespace": "^7.0.1",
-        "postcss-ordered-values": "^7.0.2",
-        "postcss-reduce-initial": "^7.0.4",
-        "postcss-reduce-transforms": "^7.0.1",
-        "postcss-svgo": "^7.1.0",
-        "postcss-unique-selectors": "^7.0.4"
-      },
-      "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss-colormin": "^8.0.1",
+        "postcss-convert-values": "^8.0.1",
+        "postcss-discard-comments": "^8.0.1",
+        "postcss-discard-duplicates": "^8.0.1",
+        "postcss-discard-empty": "^8.0.1",
+        "postcss-discard-overridden": "^8.0.1",
+        "postcss-merge-longhand": "^8.0.1",
+        "postcss-merge-rules": "^8.0.1",
+        "postcss-minify-font-values": "^8.0.1",
+        "postcss-minify-gradients": "^8.0.1",
+        "postcss-minify-params": "^8.0.1",
+        "postcss-minify-selectors": "^8.0.2",
+        "postcss-normalize-charset": "^8.0.1",
+        "postcss-normalize-display-values": "^8.0.1",
+        "postcss-normalize-positions": "^8.0.1",
+        "postcss-normalize-repeat-style": "^8.0.1",
+        "postcss-normalize-string": "^8.0.1",
+        "postcss-normalize-timing-functions": "^8.0.1",
+        "postcss-normalize-unicode": "^8.0.1",
+        "postcss-normalize-url": "^8.0.1",
+        "postcss-normalize-whitespace": "^8.0.1",
+        "postcss-ordered-values": "^8.0.1",
+        "postcss-reduce-initial": "^8.0.1",
+        "postcss-reduce-transforms": "^8.0.1",
+        "postcss-svgo": "^8.0.1",
+        "postcss-unique-selectors": "^8.0.1"
+      },
+      "engines": {
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/cssnano-utils": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.1.tgz",
-      "integrity": "sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-6.0.1.tgz",
+      "integrity": "sha512-zk65GIxA8tCjqVk7nTm1mE+ZKxtnxAvU5JSUaBLXbAr3ZF7IOvz3fbPOnEDvZKhnS7GOIitXTS5BgehLzNoc8Q==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/csso": {
@@ -10150,37 +14148,24 @@
       "dev": true,
       "license": "CC0-1.0"
     },
-    "node_modules/cssom": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
-      "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/cssstyle": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
-      "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+      "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cssom": "~0.3.6"
+        "@asamuzakjp/css-color": "^3.2.0",
+        "rrweb-cssom": "^0.8.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       }
     },
-    "node_modules/cssstyle/node_modules/cssom": {
-      "version": "0.3.8",
-      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
-      "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/csstype": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
-      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
       "license": "MIT"
     },
     "node_modules/cwd": {
@@ -10215,18 +14200,17 @@
       }
     },
     "node_modules/data-urls": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
-      "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "abab": "^2.0.6",
-        "whatwg-mimetype": "^3.0.0",
-        "whatwg-url": "^11.0.0"
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/data-view-buffer": {
@@ -10301,13 +14285,13 @@
       "license": "MIT"
     },
     "node_modules/debug": {
-      "version": "4.3.5",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
-      "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ms": "2.1.2"
+        "ms": "^2.1.3"
       },
       "engines": {
         "node": ">=6.0"
@@ -10356,9 +14340,9 @@
       }
     },
     "node_modules/decimal.js": {
-      "version": "10.4.3",
-      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
-      "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+      "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
       "dev": true,
       "license": "MIT"
     },
@@ -10392,9 +14376,9 @@
       }
     },
     "node_modules/dedent": {
-      "version": "1.5.3",
-      "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
-      "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
+      "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
       "dev": true,
       "license": "MIT",
       "peerDependencies": {
@@ -10530,65 +14514,6 @@
         "node": ">= 14"
       }
     },
-    "node_modules/del": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
-      "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/glob": "^7.1.1",
-        "globby": "^6.1.0",
-        "is-path-cwd": "^2.0.0",
-        "is-path-in-cwd": "^2.0.0",
-        "p-map": "^2.0.0",
-        "pify": "^4.0.1",
-        "rimraf": "^2.6.3"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/del/node_modules/array-union": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-      "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-uniq": "^1.0.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/del/node_modules/globby": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
-      "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-union": "^1.0.1",
-        "glob": "^7.0.3",
-        "object-assign": "^4.0.1",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/del/node_modules/globby/node_modules/pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -10643,6 +14568,7 @@
       "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
       "dev": true,
       "license": "Apache-2.0",
+      "optional": true,
       "engines": {
         "node": ">=8"
       }
@@ -10665,9 +14591,9 @@
       "license": "MIT"
     },
     "node_modules/devtools-protocol": {
-      "version": "0.0.1155343",
-      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1155343.tgz",
-      "integrity": "sha512-oD9vGBV2wTc7fAzAM6KC0chSgs234V8+qDEeK+mcbRj2UvcuA7lgBztGi/opj/iahcXD3BSj8Ymvib628yy9FA==",
+      "version": "0.0.1507524",
+      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz",
+      "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==",
       "dev": true,
       "license": "BSD-3-Clause"
     },
@@ -10681,6 +14607,13 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
+    "node_modules/diff3": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz",
+      "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/dir-glob": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -10721,16 +14654,16 @@
       }
     },
     "node_modules/doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "esutils": "^2.0.2"
       },
       "engines": {
-        "node": ">=6.0.0"
+        "node": ">=0.10.0"
       }
     },
     "node_modules/dom-serializer": {
@@ -10761,20 +14694,6 @@
       ],
       "license": "BSD-2-Clause"
     },
-    "node_modules/domexception": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
-      "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
-      "deprecated": "Use your platform's native DOMException instead",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
     "node_modules/domhandler": {
       "version": "5.0.3",
       "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
@@ -10868,6 +14787,16 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -10876,9 +14805,9 @@
       "license": "MIT"
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.228",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz",
-      "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==",
+      "version": "1.5.378",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz",
+      "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==",
       "dev": true,
       "license": "ISC"
     },
@@ -10902,6 +14831,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/emojilib": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
+      "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/emojis-list": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
@@ -10942,60 +14878,213 @@
         "once": "^1.4.0"
       }
     },
-    "node_modules/enhanced-resolve": {
-      "version": "5.18.3",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
-      "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+    "node_modules/enhanced-resolve": {
+      "version": "5.21.6",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
+      "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.3.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/enquirer": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+      "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-colors": "^4.1.1",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "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"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/env-ci": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.2.0.tgz",
+      "integrity": "sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^8.0.0",
+        "java-properties": "^1.0.2"
+      },
+      "engines": {
+        "node": "^18.17 || >=20.6.1"
+      }
+    },
+    "node_modules/env-ci/node_modules/execa": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+      "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^8.0.1",
+        "human-signals": "^5.0.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^4.1.0",
+        "strip-final-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.17"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/env-ci/node_modules/get-stream": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+      "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/env-ci/node_modules/human-signals": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+      "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=16.17.0"
+      }
+    },
+    "node_modules/env-ci/node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/env-ci/node_modules/mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/env-ci/node_modules/npm-run-path": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+      "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/env-ci/node_modules/onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "graceful-fs": "^4.2.4",
-        "tapable": "^2.2.0"
+        "mimic-fn": "^4.0.0"
       },
       "engines": {
-        "node": ">=10.13.0"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/enquirer": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
-      "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+    "node_modules/env-ci/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "ansi-colors": "^4.1.1",
-        "strip-ansi": "^6.0.1"
-      },
       "engines": {
-        "node": ">=8.6"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/entities": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
-      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+    "node_modules/env-ci/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
       "dev": true,
-      "license": "BSD-2-Clause",
+      "license": "ISC",
       "engines": {
-        "node": ">=0.12"
+        "node": ">=14"
       },
       "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/env-ci": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz",
-      "integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==",
+    "node_modules/env-ci/node_modules/strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "execa": "^5.0.0",
-        "fromentries": "^1.3.2",
-        "java-properties": "^1.0.0"
+      "engines": {
+        "node": ">=12"
       },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true,
+      "license": "MIT",
       "engines": {
-        "node": ">=10.17"
+        "node": ">=6"
       }
     },
     "node_modules/envinfo": {
@@ -11011,6 +15100,19 @@
         "node": ">=4"
       }
     },
+    "node_modules/environment": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+      "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -11032,9 +15134,9 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.23.10",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz",
-      "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==",
+      "version": "1.24.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
+      "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -11065,7 +15167,9 @@
         "is-array-buffer": "^3.0.5",
         "is-callable": "^1.2.7",
         "is-data-view": "^1.0.2",
+        "is-negative-zero": "^2.0.3",
         "is-regex": "^1.2.1",
+        "is-set": "^2.0.3",
         "is-shared-array-buffer": "^1.0.4",
         "is-string": "^1.1.1",
         "is-typed-array": "^1.1.15",
@@ -11080,6 +15184,7 @@
         "safe-push-apply": "^1.0.0",
         "safe-regex-test": "^1.1.0",
         "set-proto": "^1.0.0",
+        "stop-iteration-iterator": "^1.1.0",
         "string.prototype.trim": "^1.2.10",
         "string.prototype.trimend": "^1.0.9",
         "string.prototype.trimstart": "^1.0.8",
@@ -11118,28 +15223,28 @@
       }
     },
     "node_modules/es-iterator-helpers": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
-      "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz",
+      "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
+        "call-bind": "^1.0.9",
+        "call-bound": "^1.0.4",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.6",
+        "es-abstract": "^1.24.2",
         "es-errors": "^1.3.0",
-        "es-set-tostringtag": "^2.0.3",
+        "es-set-tostringtag": "^2.1.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.6",
+        "get-intrinsic": "^1.3.0",
         "globalthis": "^1.0.4",
         "gopd": "^1.2.0",
         "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.2.0",
         "has-symbols": "^1.1.0",
         "internal-slot": "^1.1.0",
-        "iterator.prototype": "^1.1.4",
-        "safe-array-concat": "^1.1.3"
+        "iterator.prototype": "^1.1.5",
+        "math-intrinsics": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -11275,182 +15380,120 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
-      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
-      "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
+      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
-        "@eslint-community/eslint-utils": "^4.2.0",
-        "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.57.1",
-        "@humanwhocodes/config-array": "^0.13.0",
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.2",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.5",
+        "@eslint/js": "9.39.4",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
-        "@nodelib/fs.walk": "^1.2.8",
-        "@ungap/structured-clone": "^1.2.0",
-        "ajv": "^6.12.4",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.14.0",
         "chalk": "^4.0.0",
-        "cross-spawn": "^7.0.2",
+        "cross-spawn": "^7.0.6",
         "debug": "^4.3.2",
-        "doctrine": "^3.0.0",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1",
-        "esquery": "^1.4.2",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
+        "file-entry-cache": "^8.0.0",
         "find-up": "^5.0.0",
         "glob-parent": "^6.0.2",
-        "globals": "^13.19.0",
-        "graphemer": "^1.4.0",
         "ignore": "^5.2.0",
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
-        "is-path-inside": "^3.0.3",
-        "js-yaml": "^4.1.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
         "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.2",
+        "minimatch": "^3.1.5",
         "natural-compare": "^1.4.0",
-        "optionator": "^0.9.3",
-        "strip-ansi": "^6.0.1",
-        "text-table": "^0.2.0"
+        "optionator": "^0.9.3"
       },
       "bin": {
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://opencollective.com/eslint"
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
       }
     },
     "node_modules/eslint-config-prettier": {
-      "version": "8.10.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz",
-      "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==",
+      "version": "10.1.8",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+      "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
       "dev": true,
       "license": "MIT",
       "bin": {
         "eslint-config-prettier": "bin/cli.js"
       },
+      "funding": {
+        "url": "https://opencollective.com/eslint-config-prettier"
+      },
       "peerDependencies": {
         "eslint": ">=7.0.0"
       }
     },
-    "node_modules/eslint-config-wordpress": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-wordpress/-/eslint-config-wordpress-2.0.0.tgz",
-      "integrity": "sha512-9ydUZ1zORI3av5EOx4zQml8WpdDx1bAOZC4dLPcYGqVcdBol3dQ2L40e2ill52k/+I+rqUJppGzWK+zP7+lI1w==",
-      "deprecated": "This package has been deprecated, please use @wordpress/eslint-plugin or @wordpress/scripts",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4.2.1"
-      }
-    },
-    "node_modules/eslint-import-resolver-node": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
-      "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7",
-        "is-core-module": "^2.13.0",
-        "resolve": "^1.22.4"
-      }
-    },
-    "node_modules/eslint-import-resolver-node/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-module-utils": {
-      "version": "2.12.0",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
-      "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+    "node_modules/eslint-import-context": {
+      "version": "0.1.9",
+      "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
+      "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "debug": "^3.2.7"
+        "get-tsconfig": "^4.10.1",
+        "stable-hash-x": "^0.2.0"
       },
       "engines": {
-        "node": ">=4"
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-import-context"
+      },
+      "peerDependencies": {
+        "unrs-resolver": "^1.0.0"
       },
       "peerDependenciesMeta": {
-        "eslint": {
+        "unrs-resolver": {
           "optional": true
         }
       }
     },
-    "node_modules/eslint-module-utils/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
-      "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
+    "node_modules/eslint-import-resolver-node": {
+      "version": "0.3.10",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz",
+      "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@rtsao/scc": "^1.1.0",
-        "array-includes": "^3.1.8",
-        "array.prototype.findlastindex": "^1.2.5",
-        "array.prototype.flat": "^1.3.2",
-        "array.prototype.flatmap": "^1.3.2",
         "debug": "^3.2.7",
-        "doctrine": "^2.1.0",
-        "eslint-import-resolver-node": "^0.3.9",
-        "eslint-module-utils": "^2.12.0",
-        "hasown": "^2.0.2",
-        "is-core-module": "^2.15.1",
-        "is-glob": "^4.0.3",
-        "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.8",
-        "object.groupby": "^1.0.3",
-        "object.values": "^1.2.0",
-        "semver": "^6.3.1",
-        "string.prototype.trimend": "^1.0.8",
-        "tsconfig-paths": "^3.15.0"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "is-core-module": "^2.16.1",
+        "resolve": "^2.0.0-next.6"
       }
     },
-    "node_modules/eslint-plugin-import/node_modules/debug": {
+    "node_modules/eslint-import-resolver-node/node_modules/debug": {
       "version": "3.2.7",
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
       "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
@@ -11460,217 +15503,192 @@
         "ms": "^2.1.1"
       }
     },
-    "node_modules/eslint-plugin-import/node_modules/doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+    "node_modules/eslint-import-resolver-node/node_modules/resolve": {
+      "version": "2.0.0-next.7",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
+      "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
       "dependencies": {
-        "esutils": "^2.0.2"
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.2",
+        "node-exports-info": "^1.6.0",
+        "object-keys": "^1.1.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/eslint-plugin-import/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/eslint-import-resolver-typescript": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.5.tgz",
+      "integrity": "sha512-nbE5XLph6TLtGYcu/U6e6ZVXyKBhbDWK5cLGk76eJ7NdZpwf1P9EFkpt1Z01mNZNrrilsAYWKH6zUkL4reoXbw==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "debug": "^4.4.1",
+        "eslint-import-context": "^0.1.8",
+        "get-tsconfig": "^4.10.1",
+        "is-bun-module": "^2.0.0",
+        "stable-hash-x": "^0.2.0",
+        "tinyglobby": "^0.2.14",
+        "unrs-resolver": "^1.7.11"
       },
       "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/eslint-plugin-jest": {
-      "version": "27.9.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz",
-      "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/utils": "^5.10.0"
+        "node": "^16.17.0 || >=18.6.0"
       },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      "funding": {
+        "url": "https://opencollective.com/eslint-import-resolver-typescript"
       },
       "peerDependencies": {
-        "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0",
-        "eslint": "^7.0.0 || ^8.0.0",
-        "jest": "*"
+        "eslint": "*",
+        "eslint-plugin-import": "*",
+        "eslint-plugin-import-x": "*"
       },
       "peerDependenciesMeta": {
-        "@typescript-eslint/eslint-plugin": {
+        "eslint-plugin-import": {
           "optional": true
         },
-        "jest": {
+        "eslint-plugin-import-x": {
           "optional": true
         }
       }
     },
-    "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/scope-manager": {
-      "version": "5.62.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
-      "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "5.62.0",
-        "@typescript-eslint/visitor-keys": "5.62.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/types": {
-      "version": "5.62.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
-      "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
+    "node_modules/eslint-module-utils": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz",
+      "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/typescript-estree": {
-      "version": "5.62.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
-      "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
       "dependencies": {
-        "@typescript-eslint/types": "5.62.0",
-        "@typescript-eslint/visitor-keys": "5.62.0",
-        "debug": "^4.3.4",
-        "globby": "^11.1.0",
-        "is-glob": "^4.0.3",
-        "semver": "^7.3.7",
-        "tsutils": "^3.21.0"
+        "debug": "^3.2.7"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
+        "node": ">=4"
       },
       "peerDependenciesMeta": {
-        "typescript": {
+        "eslint": {
           "optional": true
         }
       }
     },
-    "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils": {
-      "version": "5.62.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
-      "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.2.0",
-        "@types/json-schema": "^7.0.9",
-        "@types/semver": "^7.3.12",
-        "@typescript-eslint/scope-manager": "5.62.0",
-        "@typescript-eslint/types": "5.62.0",
-        "@typescript-eslint/typescript-estree": "5.62.0",
-        "eslint-scope": "^5.1.1",
-        "semver": "^7.3.7"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+    "node_modules/eslint-module-utils/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "5.62.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
-      "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+    "node_modules/eslint-plugin-import": {
+      "version": "2.32.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+      "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "5.62.0",
-        "eslint-visitor-keys": "^3.3.0"
+        "@rtsao/scc": "^1.1.0",
+        "array-includes": "^3.1.9",
+        "array.prototype.findlastindex": "^1.2.6",
+        "array.prototype.flat": "^1.3.3",
+        "array.prototype.flatmap": "^1.3.3",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.9",
+        "eslint-module-utils": "^2.12.1",
+        "hasown": "^2.0.2",
+        "is-core-module": "^2.16.1",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.fromentries": "^2.0.8",
+        "object.groupby": "^1.0.3",
+        "object.values": "^1.2.1",
+        "semver": "^6.3.1",
+        "string.prototype.trimend": "^1.0.9",
+        "tsconfig-paths": "^3.15.0"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": ">=4"
       },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
+      "peerDependencies": {
+        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
       }
     },
-    "node_modules/eslint-plugin-jest/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+    "node_modules/eslint-plugin-import/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/eslint-plugin-jest/node_modules/semver": {
-      "version": "7.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
-      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+    "node_modules/eslint-plugin-jest": {
+      "version": "28.14.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.14.0.tgz",
+      "integrity": "sha512-P9s/qXSMTpRTerE2FQ0qJet2gKbcGyFTPAJipoKxmWqR6uuFqIqk8FuEfg5yBieOezVrEfAMZrEwJ6yEp+1MFQ==",
       "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^16.10.0 || ^18.12.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0",
+        "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0",
+        "jest": "*"
+      },
+      "peerDependenciesMeta": {
+        "@typescript-eslint/eslint-plugin": {
+          "optional": true
+        },
+        "jest": {
+          "optional": true
+        }
       }
     },
     "node_modules/eslint-plugin-jsdoc": {
-      "version": "46.10.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.10.1.tgz",
-      "integrity": "sha512-x8wxIpv00Y50NyweDUpa+58ffgSAI5sqe+zcZh33xphD0AVh+1kqr1ombaTRb7Fhpove1zfUuujlX9DWWBP5ag==",
+      "version": "50.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.8.0.tgz",
+      "integrity": "sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "@es-joy/jsdoccomment": "~0.41.0",
+        "@es-joy/jsdoccomment": "~0.50.2",
         "are-docs-informative": "^0.0.2",
         "comment-parser": "1.4.1",
-        "debug": "^4.3.4",
+        "debug": "^4.4.1",
         "escape-string-regexp": "^4.0.0",
-        "esquery": "^1.5.0",
-        "is-builtin-module": "^3.2.1",
-        "semver": "^7.5.4",
+        "espree": "^10.3.0",
+        "esquery": "^1.6.0",
+        "parse-imports-exports": "^0.2.4",
+        "semver": "^7.7.2",
         "spdx-expression-parse": "^4.0.0"
       },
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       },
       "peerDependencies": {
         "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
       }
     },
     "node_modules/eslint-plugin-jsdoc/node_modules/semver": {
-      "version": "7.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
-      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -11710,55 +15728,44 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
       }
     },
-    "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/eslint-plugin-playwright": {
+      "version": "2.10.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.10.4.tgz",
+      "integrity": "sha512-l0V/VxyqfFbtqCTxj5AdRn3Q6S/hIW4nKBnKZVleVbZ24N2My6Usj//ytX3dKKqAoSbvKck9YtSytfdZ5qjLuA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "globals": "^17.3.0"
       },
       "engines": {
-        "node": "*"
+        "node": ">=16.9.0"
+      },
+      "peerDependencies": {
+        "eslint": ">=8.40.0"
       }
     },
-    "node_modules/eslint-plugin-playwright": {
-      "version": "0.15.3",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.15.3.tgz",
-      "integrity": "sha512-LQMW5y0DLK5Fnpya7JR1oAYL2/7Y9wDiYw6VZqlKqcRGSgjbVKNqxraphk7ra1U3Bb5EK444xMgUlQPbMg2M1g==",
+    "node_modules/eslint-plugin-playwright/node_modules/globals": {
+      "version": "17.7.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
+      "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
       "dev": true,
       "license": "MIT",
-      "peerDependencies": {
-        "eslint": ">=7",
-        "eslint-plugin-jest": ">=25"
+      "engines": {
+        "node": ">=18"
       },
-      "peerDependenciesMeta": {
-        "eslint-plugin-jest": {
-          "optional": true
-        }
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/eslint-plugin-prettier": {
-      "version": "5.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz",
-      "integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==",
+      "version": "5.5.6",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz",
+      "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "prettier-linter-helpers": "^1.0.0",
-        "synckit": "^0.11.0"
+        "prettier-linter-helpers": "^1.0.1",
+        "synckit": "^0.11.13"
       },
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
@@ -11815,69 +15822,68 @@
       }
     },
     "node_modules/eslint-plugin-react-hooks": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
-      "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+      "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.24.4",
+        "@babel/parser": "^7.24.4",
+        "hermes-parser": "^0.25.1",
+        "zod": "^3.25.0 || ^4.0.0",
+        "zod-validation-error": "^3.5.0 || ^4.0.0"
+      },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "peerDependencies": {
-        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
       }
     },
-    "node_modules/eslint-plugin-react/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/eslint-plugin-react-hooks/node_modules/zod": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+      "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
       }
     },
-    "node_modules/eslint-plugin-react/node_modules/doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+    "node_modules/eslint-plugin-react-hooks/node_modules/zod-validation-error": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+      "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
       "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
+      "license": "MIT",
       "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "node": ">=18.0.0"
       },
-      "engines": {
-        "node": "*"
+      "peerDependencies": {
+        "zod": "^3.25.0 || ^4.0.0"
       }
     },
     "node_modules/eslint-plugin-react/node_modules/resolve": {
-      "version": "2.0.0-next.5",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
-      "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+      "version": "2.0.0-next.7",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
+      "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "is-core-module": "^2.13.0",
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.2",
+        "node-exports-info": "^1.6.0",
+        "object-keys": "^1.1.1",
         "path-parse": "^1.0.7",
         "supports-preserve-symlinks-flag": "^1.0.0"
       },
       "bin": {
         "resolve": "bin/resolve"
       },
+      "engines": {
+        "node": ">= 0.4"
+      },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -11916,64 +15922,47 @@
         "node": ">=10"
       }
     },
-    "node_modules/eslint/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true,
-      "license": "Python-2.0"
-    },
-    "node_modules/eslint/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/eslint/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+    "node_modules/eslint/node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
+      "peer": true,
       "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
+        "@types/json-schema": "^7.0.15"
       },
       "engines": {
-        "node": ">= 8"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
     "node_modules/eslint/node_modules/eslint-scope": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
-      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
       "dev": true,
       "license": "BSD-2-Clause",
+      "peer": true,
       "dependencies": {
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
       }
     },
     "node_modules/eslint/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
       "dev": true,
       "license": "Apache-2.0",
+      "peer": true,
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -11985,6 +15974,7 @@
       "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "locate-path": "^6.0.0",
         "path-exists": "^4.0.0"
@@ -11993,112 +15983,19 @@
         "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
-    "node_modules/eslint/node_modules/locate-path": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-locate": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/eslint/node_modules/p-locate": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-limit": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/eslint/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/eslint/node_modules/type-fest": {
-      "version": "0.20.2",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+    "node_modules/eslint/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
       "dev": true,
-      "license": "(MIT OR CC0-1.0)",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
       "engines": {
         "node": ">=10"
       },
@@ -12106,48 +16003,49 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/eslint/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+    "node_modules/eslint/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
       "dev": true,
-      "license": "ISC",
+      "license": "MIT",
+      "peer": true,
       "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
+        "p-limit": "^3.0.2"
       },
       "engines": {
-        "node": ">= 8"
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/espree": {
-      "version": "9.6.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
-      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "dependencies": {
-        "acorn": "^8.9.0",
+        "acorn": "^8.15.0",
         "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^3.4.1"
+        "eslint-visitor-keys": "^4.2.1"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
       }
     },
     "node_modules/espree/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
@@ -12168,9 +16066,9 @@
       }
     },
     "node_modules/esquery": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
-      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+      "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
@@ -12223,6 +16121,16 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/eventemitter2": {
       "version": "0.4.14",
       "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
@@ -12247,6 +16155,16 @@
         "node": ">=0.8.x"
       }
     },
+    "node_modules/events-universal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+      "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bare-events": "^2.7.0"
+      }
+    },
     "node_modules/execa": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -12271,60 +16189,6 @@
         "url": "https://github.com/sindresorhus/execa?sponsor=1"
       }
     },
-    "node_modules/execa/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/execa/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/execa/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/execa/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
     "node_modules/exit": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -12372,40 +16236,40 @@
       "license": "MIT"
     },
     "node_modules/express": {
-      "version": "4.21.2",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
-      "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+      "version": "4.22.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
+      "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "accepts": "~1.3.8",
         "array-flatten": "1.1.1",
-        "body-parser": "1.20.3",
-        "content-disposition": "0.5.4",
+        "body-parser": "~1.20.5",
+        "content-disposition": "~0.5.4",
         "content-type": "~1.0.4",
-        "cookie": "0.7.1",
-        "cookie-signature": "1.0.6",
+        "cookie": "~0.7.1",
+        "cookie-signature": "~1.0.6",
         "debug": "2.6.9",
         "depd": "2.0.0",
         "encodeurl": "~2.0.0",
         "escape-html": "~1.0.3",
         "etag": "~1.8.1",
-        "finalhandler": "1.3.1",
-        "fresh": "0.5.2",
-        "http-errors": "2.0.0",
+        "finalhandler": "~1.3.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.0",
         "merge-descriptors": "1.0.3",
         "methods": "~1.1.2",
-        "on-finished": "2.4.1",
+        "on-finished": "~2.4.1",
         "parseurl": "~1.3.3",
-        "path-to-regexp": "0.1.12",
+        "path-to-regexp": "~0.1.12",
         "proxy-addr": "~2.0.7",
-        "qs": "6.13.0",
+        "qs": "~6.15.1",
         "range-parser": "~1.2.1",
         "safe-buffer": "5.2.1",
-        "send": "0.19.0",
-        "serve-static": "1.16.2",
+        "send": "~0.19.0",
+        "serve-static": "~1.16.2",
         "setprototypeof": "1.2.0",
-        "statuses": "2.0.1",
+        "statuses": "~2.0.1",
         "type-is": "~1.6.18",
         "utils-merge": "1.0.1",
         "vary": "~1.1.2"
@@ -12539,9 +16403,9 @@
       "license": "MIT"
     },
     "node_modules/fast-glob": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
-      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -12549,7 +16413,7 @@
         "@nodelib/fs.walk": "^1.2.3",
         "glob-parent": "^5.1.2",
         "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
+        "micromatch": "^4.0.8"
       },
       "engines": {
         "node": ">=8.6.0"
@@ -12582,6 +16446,64 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/fast-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
+      "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/fast-xml-builder": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",
+      "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "path-expression-matcher": "^1.5.0",
+        "xml-naming": "^0.1.0"
+      }
+    },
+    "node_modules/fast-xml-parser": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.9.3.tgz",
+      "integrity": "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@nodable/entities": "^2.2.0",
+        "fast-xml-builder": "^1.2.0",
+        "is-unsafe": "^1.0.1",
+        "path-expression-matcher": "^1.5.0",
+        "strnum": "^2.4.1",
+        "xml-naming": "^0.1.0"
+      },
+      "bin": {
+        "fxparser": "src/cli/cli.js"
+      }
+    },
     "node_modules/fastest-levenshtein": {
       "version": "1.0.16",
       "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@@ -12636,67 +16558,71 @@
       }
     },
     "node_modules/figures": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
-      "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
+      "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "escape-string-regexp": "^1.0.5"
+        "is-unicode-supported": "^2.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/figures/node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+    "node_modules/figures/node_modules/is-unicode-supported": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=0.8.0"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/file-entry-cache": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
-      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "flat-cache": "^3.0.4"
+        "flat-cache": "^4.0.0"
       },
       "engines": {
-        "node": "^10.12.0 || >=12.0.0"
+        "node": ">=16.0.0"
       }
     },
     "node_modules/filename-reserved-regex": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
-      "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-4.0.0.tgz",
+      "integrity": "sha512-9ZT504KxEQDamsOogZImAWGEN24R1uFAxU3ZS4AZqn2ooidmN68Olh7n4/RcA4lLatZztjA0ZSuxeLHVoCc8JA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=4"
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/filenamify": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
-      "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-7.0.2.tgz",
+      "integrity": "sha512-fz10TUqSZ1lG7ftW1KnRotJzMD8YRb6kaAQKpZJBLvqXXfFgIEpuazy1w2lK3zhMiBSdH/uF9LFlv5smJ2Jl1w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "filename-reserved-regex": "^2.0.0",
-        "strip-outer": "^1.0.1",
-        "trim-repeated": "^1.0.0"
+        "filename-reserved-regex": "^4.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=20"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -12752,21 +16678,124 @@
       "license": "MIT"
     },
     "node_modules/find-cache-dir": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
-      "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz",
+      "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "common-path-prefix": "^3.0.0",
+        "pkg-dir": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/find-up": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz",
+      "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^7.1.0",
+        "path-exists": "^5.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/locate-path": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+      "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^6.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/p-limit": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+      "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^1.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/p-locate": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+      "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/path-exists": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+      "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/pkg-dir": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz",
+      "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-cache-dir/node_modules/yocto-queue": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+      "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "commondir": "^1.0.1",
-        "make-dir": "^3.0.2",
-        "pkg-dir": "^4.1.0"
-      },
       "engines": {
-        "node": ">=8"
+        "node": ">=12.20"
       },
       "funding": {
-        "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/find-file-up": {
@@ -12804,15 +16833,15 @@
       }
     },
     "node_modules/find-process": {
-      "version": "1.4.7",
-      "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.7.tgz",
-      "integrity": "sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg==",
+      "version": "1.4.11",
+      "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.11.tgz",
+      "integrity": "sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "chalk": "^4.0.0",
-        "commander": "^5.1.0",
-        "debug": "^4.1.1"
+        "chalk": "~4.1.2",
+        "commander": "^12.1.0",
+        "loglevel": "^1.9.2"
       },
       "bin": {
         "find-process": "bin/find-process.js"
@@ -12832,17 +16861,31 @@
         "node": ">=8"
       }
     },
+    "node_modules/find-up-simple": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
+      "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/find-versions": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz",
-      "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz",
+      "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "semver-regex": "^3.1.2"
+        "semver-regex": "^4.0.5",
+        "super-regex": "^1.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -12948,48 +16991,30 @@
       }
     },
     "node_modules/flat-cache": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
-      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "flatted": "^3.2.9",
-        "keyv": "^4.5.3",
-        "rimraf": "^3.0.2"
+        "keyv": "^4.5.4"
       },
       "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/flat-cache/node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "node": ">=16"
       }
     },
     "node_modules/flatted": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
-      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+      "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
       "dev": true,
       "license": "ISC"
     },
     "node_modules/follow-redirects": {
-      "version": "1.15.6",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
-      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+      "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
       "dev": true,
       "funding": [
         {
@@ -13063,44 +17088,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/foreground-child/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/foreground-child/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/foreground-child/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/foreground-child/node_modules/signal-exit": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -13114,32 +17101,18 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/foreground-child/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
     "node_modules/form-data": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
-      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+      "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
-        "mime-types": "^2.1.12"
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.4",
+        "mime-types": "^2.1.35"
       },
       "engines": {
         "node": ">= 6"
@@ -13155,17 +17128,24 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/forwarded-parse": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz",
+      "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/fraction.js": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
-      "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
         "node": "*"
       },
       "funding": {
-        "type": "patreon",
+        "type": "github",
         "url": "https://github.com/sponsors/rawify"
       }
     },
@@ -13179,45 +17159,6 @@
         "node": ">= 0.6"
       }
     },
-    "node_modules/from2": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-      "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0"
-      }
-    },
-    "node_modules/fromentries": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
-      "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/fs-constants": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
-      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/fs-exists-sync": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
@@ -13228,6 +17169,20 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/fs-ext-extra-prebuilt": {
+      "version": "2.2.7",
+      "resolved": "https://registry.npmjs.org/fs-ext-extra-prebuilt/-/fs-ext-extra-prebuilt-2.2.7.tgz",
+      "integrity": "sha512-Q7rayYRBDIvDF01HWOwSSjoaP+05N1g+o3BXL1Zf8Frw2JkjSmi4EtvCBITuW30l6hB2m2TW1pehdh8wyU/+gw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "nan": "^2.24.0"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
     "node_modules/fs-extra": {
       "version": "11.2.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
@@ -13282,6 +17237,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/function-timeout": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz",
+      "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/function.prototype.name": {
       "version": "1.1.8",
       "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
@@ -13333,6 +17301,19 @@
         "node": "6.* || 8.* || >= 10.*"
       }
     },
+    "node_modules/get-east-asian-width": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+      "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-intrinsic": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -13439,10 +17420,23 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-tsconfig": {
+      "version": "4.14.0",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+      "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
     "node_modules/get-uri": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz",
-      "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==",
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+      "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -13552,30 +17546,6 @@
       "dev": true,
       "license": "BSD-2-Clause"
     },
-    "node_modules/glob/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/glob/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/global-modules": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz",
@@ -13727,17 +17697,10 @@
       "dev": true,
       "license": "ISC"
     },
-    "node_modules/graphemer": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
-      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/grunt": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz",
-      "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==",
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.2.tgz",
+      "integrity": "sha512-bUzh5nA/P5L66ihXTDP6J5BGnMB/8lXJXejYWSbH4Y4TvWM9t2S39sggQDYYQlx06cYcCsmu63HMYHGCIzUVfg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -13746,14 +17709,14 @@
         "exit": "~0.1.2",
         "findup-sync": "~5.0.0",
         "glob": "~7.1.6",
-        "grunt-cli": "~1.4.3",
+        "grunt-cli": "^1.4.3",
         "grunt-known-options": "~2.0.0",
         "grunt-legacy-log": "~3.0.0",
         "grunt-legacy-util": "~2.0.1",
         "iconv-lite": "~0.6.3",
         "js-yaml": "~3.14.0",
-        "minimatch": "~3.0.4",
-        "nopt": "~3.0.6"
+        "minimatch": "^3.1.5",
+        "nopt": "^5.0.0"
       },
       "bin": {
         "grunt": "bin/grunt"
@@ -13872,22 +17835,22 @@
       }
     },
     "node_modules/grunt-version": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/grunt-version/-/grunt-version-3.0.1.tgz",
-      "integrity": "sha512-GBGCQffBZVYlRAnZn5qJ+4E+u4hH0VfhYjQoLqPSfeTO1QuSoG3CR7p4dK3v/gHp3ct9kN2pZrOJnutsdbEUvA==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/grunt-version/-/grunt-version-3.0.2.tgz",
+      "integrity": "sha512-tD/RCuwRaHnKa7Mdh9Hh0lA+Ia1ossIw0R5kVQ2g64KHKB8zT7huBsdkoJ5WHgkUHC7DPGbR6v2K/N0CGVsHZg==",
       "dev": true,
       "dependencies": {
-        "grunt": "^1.4.1",
-        "semver": "^7.5.4"
+        "grunt": "^1.6.1",
+        "semver": "^7.6.3"
       },
       "engines": {
         "node": ">=10"
       }
     },
     "node_modules/grunt-version/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -13910,9 +17873,9 @@
       }
     },
     "node_modules/grunt/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -13921,9 +17884,9 @@
       }
     },
     "node_modules/grunt/node_modules/minimatch": {
-      "version": "3.0.8",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
-      "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
@@ -13957,9 +17920,9 @@
       "license": "MIT"
     },
     "node_modules/handlebars": {
-      "version": "4.7.8",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
-      "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+      "version": "4.7.9",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+      "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -14079,10 +18042,23 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/hashery": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz",
+      "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hookified": "^1.15.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
     "node_modules/hasown": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -14102,6 +18078,33 @@
         "tslib": "^2.0.3"
       }
     },
+    "node_modules/hermes-estree": {
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+      "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/hermes-parser": {
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+      "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hermes-estree": "0.25.1"
+      }
+    },
+    "node_modules/highlight.js": {
+      "version": "10.7.3",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/homedir-polyfill": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -14116,13 +18119,16 @@
       }
     },
     "node_modules/hook-std": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz",
-      "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-4.0.0.tgz",
+      "integrity": "sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/hooker": {
@@ -14134,6 +18140,13 @@
         "node": "*"
       }
     },
+    "node_modules/hookified": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz",
+      "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/hosted-git-info": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
@@ -14181,16 +18194,16 @@
       }
     },
     "node_modules/html-encoding-sniffer": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
-      "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "whatwg-encoding": "^2.0.0"
+        "whatwg-encoding": "^3.1.1"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/html-entities": {
@@ -14294,18 +18307,17 @@
       }
     },
     "node_modules/http-proxy-agent": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
-      "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@tootallnate/once": "2",
-        "agent-base": "6",
-        "debug": "4"
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
       },
       "engines": {
-        "node": ">= 6"
+        "node": ">= 14"
       }
     },
     "node_modules/http-proxy-middleware": {
@@ -14361,17 +18373,17 @@
       }
     },
     "node_modules/https-proxy-agent": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
-      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "agent-base": "6",
+        "agent-base": "^7.1.2",
         "debug": "4"
       },
       "engines": {
-        "node": ">= 6"
+        "node": ">= 14"
       }
     },
     "node_modules/human-signals": {
@@ -14454,30 +18466,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/ignore-walk/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/ignore-walk/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/image-ssim": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz",
@@ -14486,9 +18474,9 @@
       "license": "MIT"
     },
     "node_modules/immutable": {
-      "version": "4.3.6",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
-      "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==",
+      "version": "5.1.7",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.7.tgz",
+      "integrity": "sha512-47Xb+LFbZ/ZIjQMj6Q5J3IfK7PJFuqRdFOC9FpGgRTK6U2dAEVmkR9hp58qU4FpYux5YXpneDwkj2EP6lppzFA==",
       "dev": true,
       "license": "MIT"
     },
@@ -14519,27 +18507,31 @@
         "node": ">=4"
       }
     },
-    "node_modules/import-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz",
-      "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==",
+    "node_modules/import-from-esm": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+      "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=12.2"
+      "dependencies": {
+        "debug": "^4.3.4",
+        "import-meta-resolve": "^4.0.0"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+      "engines": {
+        "node": ">=18.20"
       }
     },
-    "node_modules/import-lazy": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
-      "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
+    "node_modules/import-in-the-middle": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz",
+      "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==",
       "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
+      "license": "Apache-2.0",
+      "dependencies": {
+        "acorn": "^8.14.0",
+        "acorn-import-attributes": "^1.9.5",
+        "cjs-module-lexer": "^1.2.2",
+        "module-details-from-path": "^1.0.3"
       }
     },
     "node_modules/import-local": {
@@ -14562,6 +18554,17 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/import-meta-resolve": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+      "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
     "node_modules/imurmurhash": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -14582,6 +18585,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/index-to-position": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz",
+      "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -14631,50 +18647,24 @@
       "license": "MIT"
     },
     "node_modules/intl-messageformat": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-4.4.0.tgz",
-      "integrity": "sha512-z+Bj2rS3LZSYU4+sNitdHrwnBhr0wO80ZJSW8EzKDBowwUe3Q/UsvgCGjrwa+HPzoGCLEb9HAjfJgo4j2Sac8w==",
+      "version": "10.7.18",
+      "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz",
+      "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "intl-messageformat-parser": "^1.8.1"
-      }
-    },
-    "node_modules/intl-messageformat-parser": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.8.1.tgz",
-      "integrity": "sha512-IMSCKVf0USrM/959vj3xac7s8f87sc+80Y/ipBzdKy4ifBv5Gsj2tZ41EAaURVg01QU71fYr77uA8Meh6kELbg==",
-      "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/into-stream": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz",
-      "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "from2": "^2.3.0",
-        "p-is-promise": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "@formatjs/ecma402-abstract": "2.3.6",
+        "@formatjs/fast-memoize": "2.2.7",
+        "@formatjs/icu-messageformat-parser": "2.11.4",
+        "tslib": "^2.8.0"
       }
     },
     "node_modules/ip-address": {
-      "version": "9.0.5",
-      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
-      "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
+      "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "jsbn": "1.1.0",
-        "sprintf-js": "^1.1.3"
-      },
       "engines": {
         "node": ">= 12"
       }
@@ -14837,20 +18827,27 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/is-builtin-module": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
-      "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+    "node_modules/is-bun-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+      "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "builtin-modules": "^3.3.0"
+        "semver": "^7.7.1"
+      }
+    },
+    "node_modules/is-bun-module/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
       },
       "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=10"
       }
     },
     "node_modules/is-callable": {
@@ -14867,13 +18864,13 @@
       }
     },
     "node_modules/is-core-module": {
-      "version": "2.16.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
-      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+      "version": "2.16.2",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+      "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "hasown": "^2.0.2"
+        "hasown": "^2.0.3"
       },
       "engines": {
         "node": ">= 0.4"
@@ -15066,6 +19063,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-negative-zero": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+      "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -15103,52 +19113,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/is-path-cwd": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
-      "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/is-path-in-cwd": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
-      "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-path-inside": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/is-path-in-cwd/node_modules/is-path-inside": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
-      "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-is-inside": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/is-path-inside": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
-      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/is-plain-obj": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
@@ -15291,19 +19255,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-text-path": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz",
-      "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "text-extensions": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/is-typed-array": {
       "version": "1.1.15",
       "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
@@ -15320,13 +19271,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/is-unc-path": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -15353,6 +19297,19 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/is-unsafe": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz",
+      "integrity": "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/is-weakmap": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
@@ -15446,10 +19403,98 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/isomorphic-git": {
+      "version": "1.37.6",
+      "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.37.6.tgz",
+      "integrity": "sha512-qr1NFCPsVTZ6YGqTXw0CzamnsHyH9QQ1OTEfeXIweSljRUMzuHFCJdUn0wc6OcjtTDns6knxjPb7N6LmJeftOA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "async-lock": "^1.4.1",
+        "clean-git-ref": "^2.0.1",
+        "crc-32": "^1.2.0",
+        "diff3": "0.0.3",
+        "ignore": "^5.1.4",
+        "minimisted": "^2.0.0",
+        "pako": "^1.0.10",
+        "pify": "^4.0.1",
+        "readable-stream": "^4.0.0",
+        "sha.js": "^2.4.12",
+        "simple-get": "^4.0.1"
+      },
+      "bin": {
+        "isogit": "cli.cjs"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/isomorphic-git/node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/isomorphic-git/node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/isomorphic-git/node_modules/readable-stream": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+      "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "buffer": "^6.0.3",
+        "events": "^3.3.0",
+        "process": "^0.11.10",
+        "string_decoder": "^1.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/isomorphic-git/node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
     "node_modules/issue-parser": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz",
-      "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.2.tgz",
+      "integrity": "sha512-7atWPjhGEIX3JEtMrOYd8TKzboYlq+5sNbdl9POiLYOI14G5HZiQbZP0Xj5EZdrufQVXfJlpTV0hys0CuxwxZw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -15460,7 +19505,7 @@
         "lodash.uniqby": "^4.7.0"
       },
       "engines": {
-        "node": ">=10.13"
+        "node": "^18.17 || >=20.6.1"
       }
     },
     "node_modules/istanbul-lib-coverage": {
@@ -15505,35 +19550,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/istanbul-lib-report/node_modules/make-dir": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
-      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^7.5.3"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/istanbul-lib-report/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/istanbul-lib-source-maps": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
@@ -15560,9 +19576,9 @@
       }
     },
     "node_modules/istanbul-reports": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
-      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+      "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
@@ -15580,11 +19596,15 @@
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "define-properties": "^1.2.1",
-        "get-intrinsic": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "reflect.getprototypeof": "^1.0.4",
-        "set-function-name": "^2.0.1"
+        "define-data-property": "^1.1.4",
+        "es-object-atoms": "^1.0.0",
+        "get-intrinsic": "^1.2.6",
+        "get-proto": "^1.0.0",
+        "has-symbols": "^1.1.0",
+        "set-function-name": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
       }
     },
     "node_modules/jackspeak": {
@@ -15768,18 +19788,19 @@
       }
     },
     "node_modules/jest-dev-server": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-9.0.2.tgz",
-      "integrity": "sha512-Zc/JB0IlNNrpXkhBw+h86cGrde/Mey52KvF+FER2eyrtYJTHObOwW7Iarxm3rPyTKby5+3Y2QZtl8pRz/5GCxg==",
+      "version": "10.1.4",
+      "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-10.1.4.tgz",
+      "integrity": "sha512-bGQ6sedNGtT6AFHhCVqGTXMPz7UyJi/ZrhNBgyqsP0XU9N8acCEIfqZEA22rOaZ+NdEVsaltk6tL7UT6aXfI7w==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "chalk": "^4.1.2",
         "cwd": "^0.10.0",
         "find-process": "^1.4.7",
         "prompts": "^2.4.2",
-        "spawnd": "^9.0.2",
+        "spawnd": "^10.1.4",
         "tree-kill": "^1.2.2",
-        "wait-on": "^7.2.0"
+        "wait-on": "^8.0.1"
       },
       "engines": {
         "node": ">=16"
@@ -15832,26 +19853,21 @@
       }
     },
     "node_modules/jest-environment-jsdom": {
-      "version": "29.7.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
-      "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.4.1.tgz",
+      "integrity": "sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/jsdom": "^20.0.0",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jsdom": "^20.0.0"
+        "@jest/environment": "30.4.1",
+        "@jest/environment-jsdom-abstract": "30.4.1",
+        "jsdom": "^26.1.0"
       },
       "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       },
       "peerDependencies": {
-        "canvas": "^2.5.0"
+        "canvas": "^3.0.0"
       },
       "peerDependenciesMeta": {
         "canvas": {
@@ -15859,6 +19875,202 @@
         }
       }
     },
+    "node_modules/jest-environment-jsdom/node_modules/@jest/environment": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz",
+      "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/fake-timers": "30.4.1",
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "jest-mock": "30.4.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz",
+      "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.4.1",
+        "@sinonjs/fake-timers": "^15.4.0",
+        "@types/node": "*",
+        "jest-message-util": "30.4.1",
+        "jest-mock": "30.4.1",
+        "jest-util": "30.4.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/@jest/schemas": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
+      "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sinclair/typebox": "^0.34.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/@jest/types": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz",
+      "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/pattern": "30.4.0",
+        "@jest/schemas": "30.4.1",
+        "@types/istanbul-lib-coverage": "^2.0.6",
+        "@types/istanbul-reports": "^3.0.4",
+        "@types/node": "*",
+        "@types/yargs": "^17.0.33",
+        "chalk": "^4.1.2"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/@sinclair/typebox": {
+      "version": "0.34.49",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
+      "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": {
+      "version": "15.4.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz",
+      "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@sinonjs/commons": "^3.0.1"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/ci-info": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+      "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/jest-message-util": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz",
+      "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@jest/types": "30.4.1",
+        "@types/stack-utils": "^2.0.3",
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "jest-util": "30.4.1",
+        "picomatch": "^4.0.3",
+        "pretty-format": "30.4.1",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.6"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/jest-mock": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz",
+      "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "jest-util": "30.4.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/jest-util": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz",
+      "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.4.1",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "ci-info": "^4.2.0",
+        "graceful-fs": "^4.2.11",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/picomatch": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/jest-environment-jsdom/node_modules/pretty-format": {
+      "version": "30.4.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz",
+      "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/schemas": "30.4.1",
+        "ansi-styles": "^5.2.0",
+        "react-is-18": "npm:react-is@^18.3.1",
+        "react-is-19": "npm:react-is@^19.2.5"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
     "node_modules/jest-environment-node": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
@@ -16142,9 +20354,9 @@
       }
     },
     "node_modules/jest-snapshot/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -16243,27 +20455,34 @@
       }
     },
     "node_modules/jiti": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
-      "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+      "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
       "dev": true,
       "license": "MIT",
+      "optional": true,
+      "peer": true,
       "bin": {
         "jiti": "lib/jiti-cli.mjs"
       }
     },
     "node_modules/joi": {
-      "version": "17.13.3",
-      "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
-      "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
+      "version": "18.2.3",
+      "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
+      "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "@hapi/hoek": "^9.3.0",
-        "@hapi/topo": "^5.1.0",
-        "@sideway/address": "^4.1.5",
-        "@sideway/formula": "^3.0.1",
-        "@sideway/pinpoint": "^2.0.0"
+        "@hapi/address": "^5.1.1",
+        "@hapi/formula": "^3.0.2",
+        "@hapi/hoek": "^11.0.7",
+        "@hapi/pinpoint": "^2.0.1",
+        "@hapi/tlds": "^1.1.1",
+        "@hapi/topo": "^6.0.2",
+        "@standard-schema/spec": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 20"
       }
     },
     "node_modules/jpeg-js": {
@@ -16303,17 +20522,10 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
-    "node_modules/jsbn": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
-      "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/jsdoc-type-pratt-parser": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz",
-      "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz",
+      "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -16321,44 +20533,38 @@
       }
     },
     "node_modules/jsdom": {
-      "version": "20.0.3",
-      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
-      "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "abab": "^2.0.6",
-        "acorn": "^8.8.1",
-        "acorn-globals": "^7.0.0",
-        "cssom": "^0.5.0",
-        "cssstyle": "^2.3.0",
-        "data-urls": "^3.0.2",
-        "decimal.js": "^10.4.2",
-        "domexception": "^4.0.0",
-        "escodegen": "^2.0.0",
-        "form-data": "^4.0.0",
-        "html-encoding-sniffer": "^3.0.0",
-        "http-proxy-agent": "^5.0.0",
-        "https-proxy-agent": "^5.0.1",
+      "version": "26.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
+      "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssstyle": "^4.2.1",
+        "data-urls": "^5.0.0",
+        "decimal.js": "^10.5.0",
+        "html-encoding-sniffer": "^4.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.6",
         "is-potential-custom-element-name": "^1.0.1",
-        "nwsapi": "^2.2.2",
-        "parse5": "^7.1.1",
+        "nwsapi": "^2.2.16",
+        "parse5": "^7.2.1",
+        "rrweb-cssom": "^0.8.0",
         "saxes": "^6.0.0",
         "symbol-tree": "^3.2.4",
-        "tough-cookie": "^4.1.2",
-        "w3c-xmlserializer": "^4.0.0",
+        "tough-cookie": "^5.1.1",
+        "w3c-xmlserializer": "^5.0.0",
         "webidl-conversions": "^7.0.0",
-        "whatwg-encoding": "^2.0.0",
-        "whatwg-mimetype": "^3.0.0",
-        "whatwg-url": "^11.0.0",
-        "ws": "^8.11.0",
-        "xml-name-validator": "^4.0.0"
+        "whatwg-encoding": "^3.1.1",
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.1.1",
+        "ws": "^8.18.0",
+        "xml-name-validator": "^5.0.0"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18"
       },
       "peerDependencies": {
-        "canvas": "^2.5.0"
+        "canvas": "^3.0.0"
       },
       "peerDependenciesMeta": {
         "canvas": {
@@ -16414,17 +20620,17 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+    "node_modules/json-with-bigint": {
+      "version": "3.5.8",
+      "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz",
+      "integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==",
       "dev": true,
-      "license": "ISC"
+      "license": "MIT"
     },
     "node_modules/json2php": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/json2php/-/json2php-0.0.7.tgz",
-      "integrity": "sha512-dnSoUiLAoVaMXxFsVi4CrPVYMKOuDBXTghXSmMINX44RZ8WM9cXlY7UqrQnlAcODCVO7FV3+8t/5nDKAjimLfg==",
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/json2php/-/json2php-0.0.9.tgz",
+      "integrity": "sha512-fQMYwvPsQt8hxRnCGyg1r2JVi6yL8Um0DIIawiKiMK9yhAAkcRNj5UsBWoaFvFzPpcWbgw9L6wzj+UMYA702Mw==",
       "dev": true,
       "license": "BSD"
     },
@@ -16461,31 +20667,40 @@
         "graceful-fs": "^4.1.6"
       }
     },
-    "node_modules/jsonparse": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
-      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
-      "dev": true,
-      "engines": [
-        "node >= 0.2.0"
-      ],
-      "license": "MIT"
-    },
-    "node_modules/JSONStream": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
-      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+    "node_modules/jsonwebtoken": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+      "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
       "dev": true,
-      "license": "(MIT OR Apache-2.0)",
+      "license": "MIT",
       "dependencies": {
-        "jsonparse": "^1.2.0",
-        "through": ">=2.2.7 <3"
+        "jws": "^4.0.1",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^7.5.4"
       },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
       "bin": {
-        "JSONStream": "bin.js"
+        "semver": "bin/semver.js"
       },
       "engines": {
-        "node": "*"
+        "node": ">=10"
       }
     },
     "node_modules/jsx-ast-utils": {
@@ -16504,6 +20719,29 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/jwa": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+      "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -16548,9 +20786,9 @@
       }
     },
     "node_modules/known-css-properties": {
-      "version": "0.26.0",
-      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz",
-      "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==",
+      "version": "0.37.0",
+      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz",
+      "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==",
       "dev": true,
       "license": "MIT"
     },
@@ -16595,6 +20833,13 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/legacy-javascript": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.1.tgz",
+      "integrity": "sha512-lPyntS4/aS7jpuvOlitZDFifBCb4W8L/3QU0PLbUTUj+zYah8rfVjYic88yG7ZKTxhS5h9iz7duT8oUXKszLhg==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
     "node_modules/leven": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -16702,36 +20947,36 @@
       }
     },
     "node_modules/lighthouse": {
-      "version": "10.4.0",
-      "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-10.4.0.tgz",
-      "integrity": "sha512-XQWHEWkJ8YxSPsxttBJORy5+hQrzbvGkYfeP3fJjyYKioWkF2MXfFqNK4ZuV4jL8pBu7Z91qnQP6In0bq1yXww==",
+      "version": "12.8.2",
+      "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-12.8.2.tgz",
+      "integrity": "sha512-+5SKYzVaTFj22MgoYDPNrP9tlD2/Ay7j3SxPSFD9FpPyVxGr4UtOQGKyrdZ7wCmcnBaFk0mCkPfARU3CsE0nvA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@sentry/node": "^6.17.4",
-        "axe-core": "4.7.2",
-        "chrome-launcher": "^0.15.2",
-        "configstore": "^5.0.1",
-        "csp_evaluator": "1.1.1",
-        "devtools-protocol": "0.0.1155343",
+        "@paulirish/trace_engine": "0.0.59",
+        "@sentry/node": "^9.28.1",
+        "axe-core": "^4.10.3",
+        "chrome-launcher": "^1.2.0",
+        "configstore": "^7.0.0",
+        "csp_evaluator": "1.1.5",
+        "devtools-protocol": "0.0.1507524",
         "enquirer": "^2.3.6",
         "http-link-header": "^1.1.1",
-        "intl-messageformat": "^4.4.0",
+        "intl-messageformat": "^10.5.3",
         "jpeg-js": "^0.4.4",
-        "js-library-detector": "^6.6.0",
-        "lighthouse-logger": "^1.4.1",
-        "lighthouse-stack-packs": "1.11.0",
-        "lodash": "^4.17.21",
+        "js-library-detector": "^6.7.0",
+        "lighthouse-logger": "^2.0.2",
+        "lighthouse-stack-packs": "1.12.2",
+        "lodash-es": "^4.17.21",
         "lookup-closest-locale": "6.2.0",
         "metaviewport-parser": "0.3.0",
         "open": "^8.4.0",
         "parse-cache-control": "1.0.1",
-        "ps-list": "^8.0.0",
-        "puppeteer-core": "^20.8.0",
-        "robots-parser": "^3.0.0",
-        "semver": "^5.3.0",
+        "puppeteer-core": "^24.17.1",
+        "robots-parser": "^3.0.1",
         "speedline-core": "^1.4.3",
-        "third-party-web": "^0.23.3",
+        "third-party-web": "^0.27.0",
+        "tldts-icann": "^7.0.12",
         "ws": "^7.0.0",
         "yargs": "^17.3.1",
         "yargs-parser": "^21.0.0"
@@ -16742,153 +20987,93 @@
         "smokehouse": "cli/test/smokehouse/frontends/smokehouse-bin.js"
       },
       "engines": {
-        "node": ">=16.16"
+        "node": ">=18.16"
       }
     },
     "node_modules/lighthouse-logger": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz",
-      "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz",
+      "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "debug": "^2.6.9",
+        "debug": "^4.4.1",
         "marky": "^1.2.2"
       }
     },
-    "node_modules/lighthouse-logger/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/lighthouse-logger/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/lighthouse-stack-packs": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.11.0.tgz",
-      "integrity": "sha512-sRr0z1S/I26VffRLq9KJsKtLk856YrJlNGmcJmbLX8dFn3MuzVPUbstuChEhqnSxZb8TZmVfthuXuwhG9vRoSw==",
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.12.2.tgz",
+      "integrity": "sha512-Ug8feS/A+92TMTCK6yHYLwaFMuelK/hAKRMdldYkMNwv+d9PtWxjXEg6rwKtsUXTADajhdrhXyuNCJ5/sfmPFw==",
       "dev": true,
       "license": "Apache-2.0"
     },
-    "node_modules/lighthouse/node_modules/axe-core": {
-      "version": "4.7.2",
-      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz",
-      "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==",
-      "dev": true,
-      "license": "MPL-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/lighthouse/node_modules/cross-fetch": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
-      "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "node-fetch": "^2.6.12"
-      }
-    },
-    "node_modules/lighthouse/node_modules/debug": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+    "node_modules/lighthouse/node_modules/@puppeteer/browsers": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz",
+      "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "ms": "2.1.2"
-      },
-      "engines": {
-        "node": ">=6.0"
+        "debug": "^4.4.3",
+        "extract-zip": "^2.0.1",
+        "progress": "^2.0.3",
+        "proxy-agent": "^6.5.0",
+        "semver": "^7.7.4",
+        "tar-fs": "^3.1.1",
+        "yargs": "^17.7.2"
       },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/lighthouse/node_modules/node-fetch": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
-      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
+      "bin": {
+        "browsers": "lib/cjs/main-cli.js"
       },
       "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
+        "node": ">=18"
       }
     },
     "node_modules/lighthouse/node_modules/puppeteer-core": {
-      "version": "20.9.0",
-      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz",
-      "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==",
+      "version": "24.43.1",
+      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz",
+      "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "@puppeteer/browsers": "1.4.6",
-        "chromium-bidi": "0.4.16",
-        "cross-fetch": "4.0.0",
-        "debug": "4.3.4",
-        "devtools-protocol": "0.0.1147663",
-        "ws": "8.13.0"
-      },
-      "engines": {
-        "node": ">=16.3.0"
-      },
-      "peerDependencies": {
-        "typescript": ">= 4.7.4"
+        "@puppeteer/browsers": "2.13.2",
+        "chromium-bidi": "14.0.0",
+        "debug": "^4.4.3",
+        "devtools-protocol": "0.0.1608973",
+        "typed-query-selector": "^2.12.2",
+        "webdriver-bidi-protocol": "0.4.1",
+        "ws": "^8.20.0"
       },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+      "engines": {
+        "node": ">=18"
       }
     },
     "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/chromium-bidi": {
-      "version": "0.4.16",
-      "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz",
-      "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==",
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
+      "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "mitt": "3.0.0"
+        "mitt": "^3.0.1",
+        "zod": "^3.24.1"
       },
       "peerDependencies": {
         "devtools-protocol": "*"
       }
     },
     "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/devtools-protocol": {
-      "version": "0.0.1147663",
-      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz",
-      "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==",
+      "version": "0.0.1608973",
+      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz",
+      "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==",
       "dev": true,
       "license": "BSD-3-Clause"
     },
     "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": {
-      "version": "8.13.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
-      "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
+      "version": "8.21.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+      "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -16908,44 +21093,22 @@
       }
     },
     "node_modules/lighthouse/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/lighthouse/node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lighthouse/node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/lighthouse/node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
       }
     },
     "node_modules/lighthouse/node_modules/ws": {
-      "version": "7.5.10",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
-      "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+      "version": "7.5.11",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
+      "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -16964,265 +21127,14 @@
         }
       }
     },
-    "node_modules/lightningcss": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
-      "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
-      "dev": true,
-      "license": "MPL-2.0",
-      "dependencies": {
-        "detect-libc": "^2.0.3"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      },
-      "optionalDependencies": {
-        "lightningcss-android-arm64": "1.30.2",
-        "lightningcss-darwin-arm64": "1.30.2",
-        "lightningcss-darwin-x64": "1.30.2",
-        "lightningcss-freebsd-x64": "1.30.2",
-        "lightningcss-linux-arm-gnueabihf": "1.30.2",
-        "lightningcss-linux-arm64-gnu": "1.30.2",
-        "lightningcss-linux-arm64-musl": "1.30.2",
-        "lightningcss-linux-x64-gnu": "1.30.2",
-        "lightningcss-linux-x64-musl": "1.30.2",
-        "lightningcss-win32-arm64-msvc": "1.30.2",
-        "lightningcss-win32-x64-msvc": "1.30.2"
-      }
-    },
-    "node_modules/lightningcss-android-arm64": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
-      "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-darwin-arm64": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
-      "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-darwin-x64": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
-      "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-freebsd-x64": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
-      "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-linux-arm-gnueabihf": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
-      "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-linux-arm64-gnu": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
-      "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-linux-arm64-musl": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
-      "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-linux-x64-gnu": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
-      "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-linux-x64-musl": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
-      "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-win32-arm64-msvc": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
-      "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
-      }
-    },
-    "node_modules/lightningcss-win32-x64-msvc": {
-      "version": "1.30.2",
-      "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
-      "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
-      "cpu": [
-        "x64"
-      ],
+    "node_modules/lighthouse/node_modules/zod": {
+      "version": "3.25.76",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+      "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
       "dev": true,
-      "license": "MPL-2.0",
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 12.0.0"
-      },
+      "license": "MIT",
       "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/parcel"
+        "url": "https://github.com/sponsors/colinhacks"
       }
     },
     "node_modules/lilconfig": {
@@ -17349,6 +21261,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/lodash-es": {
+      "version": "4.18.1",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+      "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lodash.capitalize": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
@@ -17356,24 +21275,45 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+    "node_modules/lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.escaperegexp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+      "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.escaperegexp": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
-      "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+    "node_modules/lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.ismatch": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
-      "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==",
+    "node_modules/lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
       "dev": true,
       "license": "MIT"
     },
@@ -17402,6 +21342,14 @@
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/lodash.truncate": {
@@ -17441,6 +21389,20 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/loglevel": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
+      "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      },
+      "funding": {
+        "type": "tidelift",
+        "url": "https://tidelift.com/funding/github/npm/loglevel"
+      }
+    },
     "node_modules/longest-streak": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz",
@@ -17490,13 +21452,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/lru_map": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
-      "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -17507,32 +21462,66 @@
         "yallist": "^3.0.2"
       }
     },
-    "node_modules/magic-string": {
-      "version": "0.30.21",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
-      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+    "node_modules/make-asynchronous": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/make-asynchronous/-/make-asynchronous-1.1.0.tgz",
+      "integrity": "sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.5.5"
+        "p-event": "^6.0.0",
+        "type-fest": "^4.6.0",
+        "web-worker": "^1.5.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-asynchronous/node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/make-dir": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "semver": "^6.0.0"
+        "semver": "^7.5.3"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=10"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/make-dir/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/make-iterator": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
@@ -17789,56 +21778,73 @@
       "license": "MIT"
     },
     "node_modules/marked": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
-      "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+      "version": "15.0.12",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
+      "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
       "dev": true,
       "license": "MIT",
       "bin": {
         "marked": "bin/marked.js"
       },
       "engines": {
-        "node": ">= 12"
+        "node": ">= 18"
       }
     },
     "node_modules/marked-terminal": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz",
-      "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz",
+      "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ansi-escapes": "^6.2.0",
-        "cardinal": "^2.1.1",
-        "chalk": "^5.2.0",
-        "cli-table3": "^0.6.3",
-        "node-emoji": "^1.11.0",
-        "supports-hyperlinks": "^2.3.0"
+        "ansi-escapes": "^7.0.0",
+        "ansi-regex": "^6.1.0",
+        "chalk": "^5.4.1",
+        "cli-highlight": "^2.1.11",
+        "cli-table3": "^0.6.5",
+        "node-emoji": "^2.2.0",
+        "supports-hyperlinks": "^3.1.0"
       },
       "engines": {
-        "node": ">=14.13.1 || >=16.0.0"
+        "node": ">=16.0.0"
       },
       "peerDependencies": {
-        "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
+        "marked": ">=1 <16"
       }
     },
     "node_modules/marked-terminal/node_modules/ansi-escapes": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
-      "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+      "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "environment": "^1.0.0"
+      },
       "engines": {
-        "node": ">=14.16"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/marked-terminal/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
     "node_modules/marked-terminal/node_modules/chalk": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
-      "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -18055,9 +22061,9 @@
       }
     },
     "node_modules/memize": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.0.tgz",
-      "integrity": "sha512-yywVJy8ctVlN5lNPxsep5urnZ6TTclwPEyigM9M3Bi8vseJBOfqNrGWN/r8NzuIt3PovM323W04blJfGQfQSVg==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz",
+      "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==",
       "dev": true,
       "license": "MIT"
     },
@@ -18367,9 +22373,9 @@
       }
     },
     "node_modules/mini-css-extract-plugin": {
-      "version": "2.9.0",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
-      "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
+      "version": "2.10.2",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz",
+      "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -18395,19 +22401,27 @@
       "license": "ISC"
     },
     "node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "brace-expansion": "^2.0.1"
+        "brace-expansion": "^1.1.7"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "node": "*"
+      }
+    },
+    "node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+      "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
     "node_modules/minimist": {
@@ -18455,20 +22469,30 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/minimisted": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz",
+      "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.5"
+      }
+    },
     "node_modules/minipass": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
-      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+      "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "engines": {
         "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/mitt": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
-      "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
       "dev": true,
       "license": "MIT"
     },
@@ -18496,22 +22520,19 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/mkdirp-classic": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
-      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+    "node_modules/module-details-from-path": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
+      "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/modify-values": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",
-      "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==",
+    "node_modules/mousetrap": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
+      "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==",
       "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
+      "license": "Apache-2.0 WITH LLVM-exception"
     },
     "node_modules/mrmime": {
       "version": "2.0.0",
@@ -18524,9 +22545,9 @@
       }
     },
     "node_modules/ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
       "dev": true,
       "license": "MIT"
     },
@@ -18554,10 +22575,29 @@
         "node": "^18.17.0 || >=20.5.0"
       }
     },
+    "node_modules/mz": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "any-promise": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "thenify-all": "^1.0.0"
+      }
+    },
+    "node_modules/nan": {
+      "version": "2.27.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz",
+      "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/nanoid": {
-      "version": "3.3.11",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
-      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "version": "3.3.15",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+      "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
       "dev": true,
       "funding": [
         {
@@ -18565,6 +22605,7 @@
           "url": "https://github.com/sponsors/ai"
         }
       ],
+      "license": "MIT",
       "bin": {
         "nanoid": "bin/nanoid.cjs"
       },
@@ -18572,6 +22613,22 @@
         "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
       }
     },
+    "node_modules/napi-postinstall": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+      "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "napi-postinstall": "lib/cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/napi-postinstall"
+      }
+    },
     "node_modules/natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -18604,9 +22661,9 @@
       "license": "MIT"
     },
     "node_modules/netmask": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
-      "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz",
+      "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -18630,14 +22687,47 @@
         "tslib": "^2.0.3"
       }
     },
+    "node_modules/node-addon-api": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+      "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
     "node_modules/node-emoji": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
-      "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
+      "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/is": "^4.6.0",
+        "char-regex": "^1.0.2",
+        "emojilib": "^2.4.0",
+        "skin-tone": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/node-exports-info": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz",
+      "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "lodash": "^4.17.21"
+        "array.prototype.flatmap": "^1.3.3",
+        "es-errors": "^1.3.0",
+        "object.entries": "^1.1.9",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
     "node_modules/node-fetch": {
@@ -18704,16 +22794,19 @@
       "license": "MIT"
     },
     "node_modules/node-releases": {
-      "version": "2.0.21",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
-      "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
+      "version": "2.0.49",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz",
+      "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/nopt": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-      "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
@@ -18721,6 +22814,9 @@
       },
       "bin": {
         "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
     "node_modules/normalize-package-data": {
@@ -18740,9 +22836,9 @@
       }
     },
     "node_modules/normalize-package-data/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -18762,16 +22858,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/normalize-range": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
-      "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/normalize-url": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
@@ -18786,26 +22872,26 @@
       }
     },
     "node_modules/npm": {
-      "version": "8.19.4",
-      "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz",
-      "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==",
+      "version": "11.17.0",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-11.17.0.tgz",
+      "integrity": "sha512-PurxiZexEHDTE4SSaLI3ZrnbAGiZfeyUcQcxcP5D+hfytNAze/D1IzDuInTn9XVLIbAQUnQuSPXJx02LHjLvQw==",
       "bundleDependencies": [
         "@isaacs/string-locale-compare",
         "@npmcli/arborist",
-        "@npmcli/ci-detect",
         "@npmcli/config",
         "@npmcli/fs",
         "@npmcli/map-workspaces",
+        "@npmcli/metavuln-calculator",
         "@npmcli/package-json",
+        "@npmcli/promise-spawn",
+        "@npmcli/redact",
         "@npmcli/run-script",
+        "@sigstore/tuf",
         "abbrev",
         "archy",
         "cacache",
         "chalk",
-        "chownr",
-        "cli-columns",
-        "cli-table3",
-        "columnify",
+        "ci-info",
         "fastest-levenshtein",
         "fs-minipass",
         "glob",
@@ -18819,7 +22905,6 @@
         "libnpmdiff",
         "libnpmexec",
         "libnpmfund",
-        "libnpmhook",
         "libnpmorg",
         "libnpmpack",
         "libnpmpublish",
@@ -18830,8 +22915,6 @@
         "minimatch",
         "minipass",
         "minipass-pipeline",
-        "mkdirp",
-        "mkdirp-infer-owner",
         "ms",
         "node-gyp",
         "nopt",
@@ -18842,116 +22925,105 @@
         "npm-profile",
         "npm-registry-fetch",
         "npm-user-validate",
-        "npmlog",
-        "opener",
         "p-map",
         "pacote",
         "parse-conflict-json",
         "proc-log",
         "qrcode-terminal",
         "read",
-        "read-package-json",
-        "read-package-json-fast",
-        "readdir-scoped-modules",
-        "rimraf",
         "semver",
+        "spdx-expression-parse",
         "ssri",
+        "supports-color",
         "tar",
         "text-table",
         "tiny-relative-date",
         "treeverse",
         "validate-npm-package-name",
-        "which",
-        "write-file-atomic"
+        "which"
       ],
       "dev": true,
       "license": "Artistic-2.0",
       "workspaces": [
         "docs",
         "smoke-tests",
+        "mock-globals",
+        "mock-registry",
         "workspaces/*"
       ],
       "dependencies": {
         "@isaacs/string-locale-compare": "^1.1.0",
-        "@npmcli/arborist": "^5.6.3",
-        "@npmcli/ci-detect": "^2.0.0",
-        "@npmcli/config": "^4.2.1",
-        "@npmcli/fs": "^2.1.0",
-        "@npmcli/map-workspaces": "^2.0.3",
-        "@npmcli/package-json": "^2.0.0",
-        "@npmcli/run-script": "^4.2.1",
-        "abbrev": "~1.1.1",
+        "@npmcli/arborist": "^9.8.0",
+        "@npmcli/config": "^10.11.0",
+        "@npmcli/fs": "^5.0.0",
+        "@npmcli/map-workspaces": "^5.0.3",
+        "@npmcli/metavuln-calculator": "^9.0.3",
+        "@npmcli/package-json": "^7.0.5",
+        "@npmcli/promise-spawn": "^9.0.1",
+        "@npmcli/redact": "^4.0.0",
+        "@npmcli/run-script": "^10.0.4",
+        "@sigstore/tuf": "^4.0.2",
+        "abbrev": "^4.0.0",
         "archy": "~1.0.0",
-        "cacache": "^16.1.3",
-        "chalk": "^4.1.2",
-        "chownr": "^2.0.0",
-        "cli-columns": "^4.0.0",
-        "cli-table3": "^0.6.2",
-        "columnify": "^1.6.0",
-        "fastest-levenshtein": "^1.0.12",
-        "fs-minipass": "^2.1.0",
-        "glob": "^8.0.1",
-        "graceful-fs": "^4.2.10",
-        "hosted-git-info": "^5.2.1",
-        "ini": "^3.0.1",
-        "init-package-json": "^3.0.2",
-        "is-cidr": "^4.0.2",
-        "json-parse-even-better-errors": "^2.3.1",
-        "libnpmaccess": "^6.0.4",
-        "libnpmdiff": "^4.0.5",
-        "libnpmexec": "^4.0.14",
-        "libnpmfund": "^3.0.5",
-        "libnpmhook": "^8.0.4",
-        "libnpmorg": "^4.0.4",
-        "libnpmpack": "^4.1.3",
-        "libnpmpublish": "^6.0.5",
-        "libnpmsearch": "^5.0.4",
-        "libnpmteam": "^4.0.4",
-        "libnpmversion": "^3.0.7",
-        "make-fetch-happen": "^10.2.0",
-        "minimatch": "^5.1.0",
-        "minipass": "^3.1.6",
+        "cacache": "^20.0.4",
+        "chalk": "^5.6.2",
+        "ci-info": "^4.4.0",
+        "fastest-levenshtein": "^1.0.16",
+        "fs-minipass": "^3.0.3",
+        "glob": "^13.0.6",
+        "graceful-fs": "^4.2.11",
+        "hosted-git-info": "^9.0.3",
+        "ini": "^6.0.0",
+        "init-package-json": "^8.2.5",
+        "is-cidr": "^6.0.4",
+        "json-parse-even-better-errors": "^5.0.0",
+        "libnpmaccess": "^10.0.3",
+        "libnpmdiff": "^8.1.10",
+        "libnpmexec": "^10.3.0",
+        "libnpmfund": "^7.0.24",
+        "libnpmorg": "^8.0.1",
+        "libnpmpack": "^9.1.10",
+        "libnpmpublish": "^11.2.0",
+        "libnpmsearch": "^9.0.1",
+        "libnpmteam": "^8.0.2",
+        "libnpmversion": "^8.0.4",
+        "make-fetch-happen": "^15.0.6",
+        "minimatch": "^10.2.5",
+        "minipass": "^7.1.3",
         "minipass-pipeline": "^1.2.4",
-        "mkdirp": "^1.0.4",
-        "mkdirp-infer-owner": "^2.0.0",
         "ms": "^2.1.2",
-        "node-gyp": "^9.1.0",
-        "nopt": "^6.0.0",
-        "npm-audit-report": "^3.0.0",
-        "npm-install-checks": "^5.0.0",
-        "npm-package-arg": "^9.1.0",
-        "npm-pick-manifest": "^7.0.2",
-        "npm-profile": "^6.2.0",
-        "npm-registry-fetch": "^13.3.1",
-        "npm-user-validate": "^1.0.1",
-        "npmlog": "^6.0.2",
-        "opener": "^1.5.2",
-        "p-map": "^4.0.0",
-        "pacote": "^13.6.2",
-        "parse-conflict-json": "^2.0.2",
-        "proc-log": "^2.0.1",
+        "node-gyp": "^12.4.0",
+        "nopt": "^9.0.0",
+        "npm-audit-report": "^7.0.0",
+        "npm-install-checks": "^8.0.0",
+        "npm-package-arg": "^13.0.2",
+        "npm-pick-manifest": "^11.0.3",
+        "npm-profile": "^12.0.1",
+        "npm-registry-fetch": "^19.1.1",
+        "npm-user-validate": "^4.0.0",
+        "p-map": "^7.0.4",
+        "pacote": "^21.5.1",
+        "parse-conflict-json": "^5.0.1",
+        "proc-log": "^6.1.0",
         "qrcode-terminal": "^0.12.0",
-        "read": "~1.0.7",
-        "read-package-json": "^5.0.2",
-        "read-package-json-fast": "^2.0.3",
-        "readdir-scoped-modules": "^1.1.0",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.7",
-        "ssri": "^9.0.1",
-        "tar": "^6.1.11",
+        "read": "^5.0.1",
+        "semver": "^7.8.4",
+        "spdx-expression-parse": "^4.0.0",
+        "ssri": "^13.0.1",
+        "supports-color": "^10.2.2",
+        "tar": "^7.5.16",
         "text-table": "~0.2.0",
-        "tiny-relative-date": "^1.3.0",
-        "treeverse": "^2.0.0",
-        "validate-npm-package-name": "^4.0.0",
-        "which": "^2.0.2",
-        "write-file-atomic": "^4.0.1"
+        "tiny-relative-date": "^2.0.2",
+        "treeverse": "^3.0.0",
+        "validate-npm-package-name": "^7.0.2",
+        "which": "^6.0.1"
       },
       "bin": {
         "npm": "bin/npm-cli.js",
         "npx": "bin/npx-cli.js"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm-bundled": {
@@ -19012,9 +23084,9 @@
       "license": "MIT"
     },
     "node_modules/npm-package-json-lint/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
       "dev": true,
       "license": "ISC",
       "bin": {
@@ -19095,17 +23167,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/npm-run-all/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
     "node_modules/npm-run-all/node_modules/chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -19175,19 +23236,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/npm-run-all/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/npm-run-all/node_modules/path-key": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@@ -19234,21 +23282,26 @@
         "node": ">=8"
       }
     },
-    "node_modules/npm/node_modules/@colors/colors": {
-      "version": "1.5.0",
+    "node_modules/npm/node_modules/@gar/promise-retry": {
+      "version": "1.0.3",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
-      "optional": true,
       "engines": {
-        "node": ">=0.1.90"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@gar/promisify": {
-      "version": "1.1.3",
+    "node_modules/npm/node_modules/@isaacs/fs-minipass": {
+      "version": "4.0.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT"
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^7.0.4"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
     },
     "node_modules/npm/node_modules/@isaacs/string-locale-compare": {
       "version": "1.1.0",
@@ -19256,616 +23309,510 @@
       "inBundle": true,
       "license": "ISC"
     },
-    "node_modules/npm/node_modules/@npmcli/arborist": {
-      "version": "5.6.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "@isaacs/string-locale-compare": "^1.1.0",
-        "@npmcli/installed-package-contents": "^1.0.7",
-        "@npmcli/map-workspaces": "^2.0.3",
-        "@npmcli/metavuln-calculator": "^3.0.1",
-        "@npmcli/move-file": "^2.0.0",
-        "@npmcli/name-from-folder": "^1.0.1",
-        "@npmcli/node-gyp": "^2.0.0",
-        "@npmcli/package-json": "^2.0.0",
-        "@npmcli/query": "^1.2.0",
-        "@npmcli/run-script": "^4.1.3",
-        "bin-links": "^3.0.3",
-        "cacache": "^16.1.3",
-        "common-ancestor-path": "^1.0.1",
-        "hosted-git-info": "^5.2.1",
-        "json-parse-even-better-errors": "^2.3.1",
-        "json-stringify-nice": "^1.1.4",
-        "minimatch": "^5.1.0",
-        "mkdirp": "^1.0.4",
-        "mkdirp-infer-owner": "^2.0.0",
-        "nopt": "^6.0.0",
-        "npm-install-checks": "^5.0.0",
-        "npm-package-arg": "^9.0.0",
-        "npm-pick-manifest": "^7.0.2",
-        "npm-registry-fetch": "^13.0.0",
-        "npmlog": "^6.0.2",
-        "pacote": "^13.6.1",
-        "parse-conflict-json": "^2.0.1",
-        "proc-log": "^2.0.0",
-        "promise-all-reject-late": "^1.0.0",
-        "promise-call-limit": "^1.0.1",
-        "read-package-json-fast": "^2.0.2",
-        "readdir-scoped-modules": "^1.1.0",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.7",
-        "ssri": "^9.0.0",
-        "treeverse": "^2.0.0",
-        "walk-up-path": "^1.0.0"
-      },
-      "bin": {
-        "arborist": "bin/index.js"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/@npmcli/ci-detect": {
-      "version": "2.0.0",
+    "node_modules/npm/node_modules/@npmcli/agent": {
+      "version": "4.0.2",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.1",
+        "lru-cache": "^11.2.1",
+        "socks-proxy-agent": "^8.0.3"
+      },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@npmcli/config": {
-      "version": "4.2.2",
+    "node_modules/npm/node_modules/@npmcli/arborist": {
+      "version": "9.8.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/map-workspaces": "^2.0.2",
-        "ini": "^3.0.0",
-        "mkdirp-infer-owner": "^2.0.0",
-        "nopt": "^6.0.0",
-        "proc-log": "^2.0.0",
-        "read-package-json-fast": "^2.0.3",
-        "semver": "^7.3.5",
-        "walk-up-path": "^1.0.0"
+        "@gar/promise-retry": "^1.0.0",
+        "@isaacs/string-locale-compare": "^1.1.0",
+        "@npmcli/fs": "^5.0.0",
+        "@npmcli/installed-package-contents": "^4.0.0",
+        "@npmcli/map-workspaces": "^5.0.0",
+        "@npmcli/metavuln-calculator": "^9.0.2",
+        "@npmcli/name-from-folder": "^4.0.0",
+        "@npmcli/node-gyp": "^5.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/query": "^5.0.0",
+        "@npmcli/redact": "^4.0.0",
+        "@npmcli/run-script": "^10.0.0",
+        "bin-links": "^6.0.0",
+        "cacache": "^20.0.1",
+        "common-ancestor-path": "^2.0.0",
+        "hosted-git-info": "^9.0.0",
+        "json-stringify-nice": "^1.1.4",
+        "lru-cache": "^11.2.1",
+        "minimatch": "^10.0.3",
+        "nopt": "^9.0.0",
+        "npm-install-checks": "^8.0.0",
+        "npm-package-arg": "^13.0.0",
+        "npm-pick-manifest": "^11.0.1",
+        "npm-registry-fetch": "^19.0.0",
+        "pacote": "^21.0.2",
+        "parse-conflict-json": "^5.0.1",
+        "proc-log": "^6.0.0",
+        "proggy": "^4.0.0",
+        "promise-all-reject-late": "^1.0.0",
+        "promise-call-limit": "^3.0.1",
+        "semver": "^7.3.7",
+        "ssri": "^13.0.0",
+        "treeverse": "^3.0.0",
+        "walk-up-path": "^4.0.0"
+      },
+      "bin": {
+        "arborist": "bin/index.js"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@npmcli/disparity-colors": {
-      "version": "2.0.0",
+    "node_modules/npm/node_modules/@npmcli/config": {
+      "version": "10.11.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "ansi-styles": "^4.3.0"
+        "@npmcli/map-workspaces": "^5.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "ci-info": "^4.0.0",
+        "ini": "^6.0.0",
+        "nopt": "^9.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.3.5",
+        "walk-up-path": "^4.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/fs": {
-      "version": "2.1.2",
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@gar/promisify": "^1.1.3",
         "semver": "^7.3.5"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/git": {
-      "version": "3.0.2",
+      "version": "7.0.2",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/promise-spawn": "^3.0.0",
-        "lru-cache": "^7.4.4",
-        "mkdirp": "^1.0.4",
-        "npm-pick-manifest": "^7.0.0",
-        "proc-log": "^2.0.0",
-        "promise-inflight": "^1.0.1",
-        "promise-retry": "^2.0.1",
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "ini": "^6.0.0",
+        "lru-cache": "^11.2.1",
+        "npm-pick-manifest": "^11.0.1",
+        "proc-log": "^6.0.0",
         "semver": "^7.3.5",
-        "which": "^2.0.2"
+        "which": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/installed-package-contents": {
-      "version": "1.0.7",
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-bundled": "^1.1.1",
-        "npm-normalize-package-bin": "^1.0.1"
+        "npm-bundled": "^5.0.0",
+        "npm-normalize-package-bin": "^5.0.0"
       },
       "bin": {
-        "installed-package-contents": "index.js"
+        "installed-package-contents": "bin/index.js"
       },
       "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": {
-      "version": "1.1.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "npm-normalize-package-bin": "^1.0.1"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/map-workspaces": {
-      "version": "2.0.4",
+      "version": "5.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/name-from-folder": "^1.0.1",
-        "glob": "^8.0.1",
-        "minimatch": "^5.0.1",
-        "read-package-json-fast": "^2.0.3"
+        "@npmcli/name-from-folder": "^4.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "glob": "^13.0.0",
+        "minimatch": "^10.0.3"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
-      "version": "3.1.1",
+      "version": "9.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "cacache": "^16.0.0",
-        "json-parse-even-better-errors": "^2.3.1",
-        "pacote": "^13.0.3",
+        "cacache": "^20.0.0",
+        "json-parse-even-better-errors": "^5.0.0",
+        "pacote": "^21.0.0",
+        "proc-log": "^6.0.0",
         "semver": "^7.3.5"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@npmcli/move-file": {
-      "version": "2.0.1",
+    "node_modules/npm/node_modules/@npmcli/name-from-folder": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "mkdirp": "^1.0.4",
-        "rimraf": "^3.0.2"
-      },
+      "license": "ISC",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@npmcli/name-from-folder": {
-      "version": "1.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
     "node_modules/npm/node_modules/@npmcli/node-gyp": {
-      "version": "2.0.0",
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/package-json": {
-      "version": "2.0.0",
+      "version": "7.0.5",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "json-parse-even-better-errors": "^2.3.1"
+        "@npmcli/git": "^7.0.0",
+        "glob": "^13.0.0",
+        "hosted-git-info": "^9.0.0",
+        "json-parse-even-better-errors": "^5.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.5.3",
+        "spdx-expression-parse": "^4.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/promise-spawn": {
-      "version": "3.0.0",
+      "version": "9.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "infer-owner": "^1.0.4"
+        "which": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/@npmcli/query": {
-      "version": "1.2.0",
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-package-arg": "^9.1.0",
-        "postcss-selector-parser": "^6.0.10",
-        "semver": "^7.3.7"
+        "postcss-selector-parser": "^7.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/@npmcli/run-script": {
-      "version": "4.2.1",
+    "node_modules/npm/node_modules/@npmcli/redact": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
-      "dependencies": {
-        "@npmcli/node-gyp": "^2.0.0",
-        "@npmcli/promise-spawn": "^3.0.0",
-        "node-gyp": "^9.0.0",
-        "read-package-json-fast": "^2.0.3",
-        "which": "^2.0.2"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/@tootallnate/once": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
       "engines": {
-        "node": ">= 10"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/abbrev": {
-      "version": "1.1.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/agent-base": {
-      "version": "6.0.2",
+    "node_modules/npm/node_modules/@npmcli/run-script": {
+      "version": "10.0.4",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "debug": "4"
+        "@npmcli/node-gyp": "^5.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "node-gyp": "^12.1.0",
+        "proc-log": "^6.0.0"
       },
       "engines": {
-        "node": ">= 6.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/agentkeepalive": {
-      "version": "4.2.1",
+    "node_modules/npm/node_modules/@sigstore/bundle": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "debug": "^4.1.0",
-        "depd": "^1.1.2",
-        "humanize-ms": "^1.2.1"
+        "@sigstore/protobuf-specs": "^0.5.0"
       },
       "engines": {
-        "node": ">= 8.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/aggregate-error": {
-      "version": "3.1.0",
+    "node_modules/npm/node_modules/@sigstore/core": {
+      "version": "3.2.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "clean-stack": "^2.0.0",
-        "indent-string": "^4.0.0"
-      },
+      "license": "Apache-2.0",
       "engines": {
-        "node": ">=8"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/ansi-regex": {
-      "version": "5.0.1",
+    "node_modules/npm/node_modules/@sigstore/protobuf-specs": {
+      "version": "0.5.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "engines": {
-        "node": ">=8"
+        "node": "^18.17.0 || >=20.5.0"
       }
     },
-    "node_modules/npm/node_modules/ansi-styles": {
-      "version": "4.3.0",
+    "node_modules/npm/node_modules/@sigstore/sign": {
+      "version": "4.1.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "color-convert": "^2.0.1"
+        "@gar/promise-retry": "^1.0.2",
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.2.0",
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "make-fetch-happen": "^15.0.4",
+        "proc-log": "^6.1.0"
       },
       "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/aproba": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/archy": {
-      "version": "1.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/are-we-there-yet": {
-      "version": "3.0.1",
+    "node_modules/npm/node_modules/@sigstore/tuf": {
+      "version": "4.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "Apache-2.0",
       "dependencies": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^3.6.0"
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "tuf-js": "^4.1.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/asap": {
-      "version": "2.0.6",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/balanced-match": {
-      "version": "1.0.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/bin-links": {
-      "version": "3.0.3",
+    "node_modules/npm/node_modules/@sigstore/verify": {
+      "version": "3.1.1",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "Apache-2.0",
       "dependencies": {
-        "cmd-shim": "^5.0.0",
-        "mkdirp-infer-owner": "^2.0.0",
-        "npm-normalize-package-bin": "^2.0.0",
-        "read-cmd-shim": "^3.0.0",
-        "rimraf": "^3.0.0",
-        "write-file-atomic": "^4.0.0"
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.2.1",
+        "@sigstore/protobuf-specs": "^0.5.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": {
+    "node_modules/npm/node_modules/@tufjs/canonical-json": {
       "version": "2.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/binary-extensions": {
-      "version": "2.2.0",
-      "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/npm/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
+        "node": "^16.14.0 || >=18.0.0"
       }
     },
-    "node_modules/npm/node_modules/builtins": {
-      "version": "5.0.1",
+    "node_modules/npm/node_modules/@tufjs/models": {
+      "version": "4.1.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "semver": "^7.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/cacache": {
-      "version": "16.1.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "@npmcli/fs": "^2.1.0",
-        "@npmcli/move-file": "^2.0.0",
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.1.0",
-        "glob": "^8.0.1",
-        "infer-owner": "^1.0.4",
-        "lru-cache": "^7.7.1",
-        "minipass": "^3.1.6",
-        "minipass-collect": "^1.0.2",
-        "minipass-flush": "^1.0.5",
-        "minipass-pipeline": "^1.2.4",
-        "mkdirp": "^1.0.4",
-        "p-map": "^4.0.0",
-        "promise-inflight": "^1.0.1",
-        "rimraf": "^3.0.2",
-        "ssri": "^9.0.0",
-        "tar": "^6.1.11",
-        "unique-filename": "^2.0.0"
+        "@tufjs/canonical-json": "2.0.0",
+        "minimatch": "^10.1.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/chalk": {
-      "version": "4.1.2",
+    "node_modules/npm/node_modules/abbrev": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
+      "license": "ISC",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/chownr": {
-      "version": "2.0.0",
+    "node_modules/npm/node_modules/agent-base": {
+      "version": "7.1.4",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "engines": {
-        "node": ">=10"
+        "node": ">= 14"
       }
     },
-    "node_modules/npm/node_modules/cidr-regex": {
-      "version": "3.1.1",
+    "node_modules/npm/node_modules/aproba": {
+      "version": "2.1.0",
       "dev": true,
       "inBundle": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "ip-regex": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
+      "license": "ISC"
     },
-    "node_modules/npm/node_modules/clean-stack": {
-      "version": "2.2.0",
+    "node_modules/npm/node_modules/archy": {
+      "version": "1.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/cli-columns": {
-      "version": "4.0.0",
+    "node_modules/npm/node_modules/balanced-match": {
+      "version": "4.0.4",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
-      "dependencies": {
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1"
-      },
       "engines": {
-        "node": ">= 10"
+        "node": "18 || 20 || >=22"
       }
     },
-    "node_modules/npm/node_modules/cli-table3": {
-      "version": "0.6.2",
+    "node_modules/npm/node_modules/bin-links": {
+      "version": "6.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "string-width": "^4.2.0"
-      },
-      "engines": {
-        "node": "10.* || >= 12.*"
-      },
-      "optionalDependencies": {
-        "@colors/colors": "1.5.0"
+        "cmd-shim": "^8.0.0",
+        "npm-normalize-package-bin": "^5.0.0",
+        "proc-log": "^6.0.0",
+        "read-cmd-shim": "^6.0.0",
+        "write-file-atomic": "^7.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/clone": {
-      "version": "1.0.4",
+    "node_modules/npm/node_modules/binary-extensions": {
+      "version": "3.1.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "engines": {
-        "node": ">=0.8"
+        "node": ">=18.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/npm/node_modules/cmd-shim": {
-      "version": "5.0.0",
+    "node_modules/npm/node_modules/brace-expansion": {
+      "version": "5.0.6",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "mkdirp-infer-owner": "^2.0.0"
+        "balanced-match": "^4.0.2"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "18 || 20 || >=22"
       }
     },
-    "node_modules/npm/node_modules/color-convert": {
-      "version": "2.0.1",
+    "node_modules/npm/node_modules/cacache": {
+      "version": "20.0.4",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "color-name": "~1.1.4"
+        "@npmcli/fs": "^5.0.0",
+        "fs-minipass": "^3.0.0",
+        "glob": "^13.0.0",
+        "lru-cache": "^11.1.0",
+        "minipass": "^7.0.3",
+        "minipass-collect": "^2.0.1",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "p-map": "^7.0.2",
+        "ssri": "^13.0.0"
       },
       "engines": {
-        "node": ">=7.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/color-name": {
-      "version": "1.1.4",
+    "node_modules/npm/node_modules/chalk": {
+      "version": "5.6.2",
       "dev": true,
       "inBundle": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
     },
-    "node_modules/npm/node_modules/color-support": {
-      "version": "1.1.3",
+    "node_modules/npm/node_modules/chownr": {
+      "version": "3.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "bin": {
-        "color-support": "bin.js"
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
       }
     },
-    "node_modules/npm/node_modules/columnify": {
-      "version": "1.6.0",
+    "node_modules/npm/node_modules/ci-info": {
+      "version": "4.4.0",
       "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
       "inBundle": true,
       "license": "MIT",
-      "dependencies": {
-        "strip-ansi": "^6.0.1",
-        "wcwidth": "^1.0.0"
-      },
       "engines": {
-        "node": ">=8.0.0"
+        "node": ">=8"
       }
     },
-    "node_modules/npm/node_modules/common-ancestor-path": {
-      "version": "1.0.1",
+    "node_modules/npm/node_modules/cidr-regex": {
+      "version": "5.0.5",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=20"
+      }
     },
-    "node_modules/npm/node_modules/concat-map": {
-      "version": "0.0.1",
+    "node_modules/npm/node_modules/cmd-shim": {
+      "version": "8.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT"
+      "license": "ISC",
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
     },
-    "node_modules/npm/node_modules/console-control-strings": {
-      "version": "1.1.0",
+    "node_modules/npm/node_modules/common-ancestor-path": {
+      "version": "2.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">= 18"
+      }
     },
     "node_modules/npm/node_modules/cssesc": {
       "version": "3.0.0",
@@ -19880,12 +23827,12 @@
       }
     },
     "node_modules/npm/node_modules/debug": {
-      "version": "4.3.4",
+      "version": "4.4.3",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "ms": "2.1.2"
+        "ms": "^2.1.3"
       },
       "engines": {
         "node": ">=6.0"
@@ -19896,57 +23843,8 @@
         }
       }
     },
-    "node_modules/npm/node_modules/debug/node_modules/ms": {
-      "version": "2.1.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/debuglog": {
-      "version": "1.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/npm/node_modules/defaults": {
-      "version": "1.0.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "clone": "^1.0.2"
-      }
-    },
-    "node_modules/npm/node_modules/delegates": {
-      "version": "1.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/depd": {
-      "version": "1.1.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/npm/node_modules/dezalgo": {
-      "version": "1.0.4",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "asap": "^2.0.0",
-        "wrappy": "1"
-      }
-    },
     "node_modules/npm/node_modules/diff": {
-      "version": "5.1.0",
+      "version": "8.0.4",
       "dev": true,
       "inBundle": true,
       "license": "BSD-3-Clause",
@@ -19954,22 +23852,6 @@
         "node": ">=0.3.1"
       }
     },
-    "node_modules/npm/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/encoding": {
-      "version": "0.1.13",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "optional": true,
-      "dependencies": {
-        "iconv-lite": "^0.6.2"
-      }
-    },
     "node_modules/npm/node_modules/env-paths": {
       "version": "2.2.1",
       "dev": true,
@@ -19979,169 +23861,102 @@
         "node": ">=6"
       }
     },
-    "node_modules/npm/node_modules/err-code": {
-      "version": "2.0.3",
+    "node_modules/npm/node_modules/exponential-backoff": {
+      "version": "3.1.3",
       "dev": true,
       "inBundle": true,
-      "license": "MIT"
+      "license": "Apache-2.0"
     },
     "node_modules/npm/node_modules/fastest-levenshtein": {
-      "version": "1.0.12",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/fs-minipass": {
-      "version": "2.1.0",
+      "version": "1.0.16",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "minipass": "^3.0.0"
-      },
+      "license": "MIT",
       "engines": {
-        "node": ">= 8"
+        "node": ">= 4.9.1"
       }
     },
-    "node_modules/npm/node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/function-bind": {
-      "version": "1.1.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/gauge": {
-      "version": "4.0.4",
+    "node_modules/npm/node_modules/fs-minipass": {
+      "version": "3.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "aproba": "^1.0.3 || ^2.0.0",
-        "color-support": "^1.1.3",
-        "console-control-strings": "^1.1.0",
-        "has-unicode": "^2.0.1",
-        "signal-exit": "^3.0.7",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1",
-        "wide-align": "^1.1.5"
+        "minipass": "^7.0.3"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
     "node_modules/npm/node_modules/glob": {
-      "version": "8.0.3",
+      "version": "13.0.6",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^5.0.1",
-        "once": "^1.3.0"
+        "minimatch": "^10.2.2",
+        "minipass": "^7.1.3",
+        "path-scurry": "^2.0.2"
       },
       "engines": {
-        "node": ">=12"
+        "node": "18 || 20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/npm/node_modules/graceful-fs": {
-      "version": "4.2.10",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/has": {
-      "version": "1.0.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "function-bind": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
-    "node_modules/npm/node_modules/has-flag": {
-      "version": "4.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/npm/node_modules/has-unicode": {
-      "version": "2.0.1",
+      "version": "4.2.11",
       "dev": true,
       "inBundle": true,
       "license": "ISC"
     },
     "node_modules/npm/node_modules/hosted-git-info": {
-      "version": "5.2.1",
+      "version": "9.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "lru-cache": "^7.5.1"
+        "lru-cache": "^11.1.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/http-cache-semantics": {
-      "version": "4.1.1",
+      "version": "4.2.0",
       "dev": true,
       "inBundle": true,
       "license": "BSD-2-Clause"
     },
     "node_modules/npm/node_modules/http-proxy-agent": {
-      "version": "5.0.0",
+      "version": "7.0.2",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "@tootallnate/once": "2",
-        "agent-base": "6",
-        "debug": "4"
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
       },
       "engines": {
-        "node": ">= 6"
+        "node": ">= 14"
       }
     },
     "node_modules/npm/node_modules/https-proxy-agent": {
-      "version": "5.0.1",
+      "version": "7.0.6",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "agent-base": "6",
+        "agent-base": "^7.1.2",
         "debug": "4"
       },
       "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/npm/node_modules/humanize-ms": {
-      "version": "1.2.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.0.0"
+        "node": ">= 14"
       }
     },
     "node_modules/npm/node_modules/iconv-lite": {
-      "version": "0.6.3",
+      "version": "0.7.2",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -20151,153 +23966,89 @@
       },
       "engines": {
         "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
       }
     },
-    "node_modules/npm/node_modules/ignore-walk": {
-      "version": "5.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "minimatch": "^5.0.1"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/imurmurhash": {
-      "version": "0.1.4",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.19"
-      }
-    },
-    "node_modules/npm/node_modules/indent-string": {
-      "version": "4.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/npm/node_modules/infer-owner": {
-      "version": "1.0.4",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/inflight": {
-      "version": "1.0.6",
+    "node_modules/npm/node_modules/ignore-walk": {
+      "version": "8.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
+        "minimatch": "^10.0.3"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/inherits": {
-      "version": "2.0.4",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
     "node_modules/npm/node_modules/ini": {
-      "version": "3.0.1",
+      "version": "6.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/init-package-json": {
-      "version": "3.0.2",
+      "version": "8.2.5",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-package-arg": "^9.0.1",
-        "promzard": "^0.3.0",
-        "read": "^1.0.7",
-        "read-package-json": "^5.0.0",
-        "semver": "^7.3.5",
-        "validate-npm-package-license": "^3.0.4",
-        "validate-npm-package-name": "^4.0.0"
+        "@npmcli/package-json": "^7.0.0",
+        "npm-package-arg": "^13.0.0",
+        "promzard": "^3.0.1",
+        "read": "^5.0.1",
+        "semver": "^7.7.2",
+        "validate-npm-package-name": "^7.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/ip": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/ip-regex": {
-      "version": "4.3.0",
+    "node_modules/npm/node_modules/ip-address": {
+      "version": "10.2.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">= 12"
       }
     },
     "node_modules/npm/node_modules/is-cidr": {
-      "version": "4.0.2",
+      "version": "6.0.4",
       "dev": true,
       "inBundle": true,
       "license": "BSD-2-Clause",
       "dependencies": {
-        "cidr-regex": "^3.1.1"
+        "cidr-regex": "^5.0.4"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=20"
       }
     },
-    "node_modules/npm/node_modules/is-core-module": {
-      "version": "2.10.0",
+    "node_modules/npm/node_modules/isexe": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "has": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=20"
       }
     },
-    "node_modules/npm/node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
+    "node_modules/npm/node_modules/json-parse-even-better-errors": {
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/is-lambda": {
-      "version": "1.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/isexe": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
     "node_modules/npm/node_modules/json-stringify-nice": {
       "version": "1.1.4",
       "dev": true,
@@ -20317,294 +24068,267 @@
       "license": "MIT"
     },
     "node_modules/npm/node_modules/just-diff": {
-      "version": "5.1.1",
+      "version": "6.0.2",
       "dev": true,
       "inBundle": true,
       "license": "MIT"
     },
     "node_modules/npm/node_modules/just-diff-apply": {
-      "version": "5.4.1",
+      "version": "5.5.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT"
     },
     "node_modules/npm/node_modules/libnpmaccess": {
-      "version": "6.0.4",
+      "version": "10.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "aproba": "^2.0.0",
-        "minipass": "^3.1.1",
-        "npm-package-arg": "^9.0.1",
-        "npm-registry-fetch": "^13.0.0"
+        "npm-package-arg": "^13.0.0",
+        "npm-registry-fetch": "^19.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmdiff": {
-      "version": "4.0.5",
+      "version": "8.1.10",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/disparity-colors": "^2.0.0",
-        "@npmcli/installed-package-contents": "^1.0.7",
-        "binary-extensions": "^2.2.0",
-        "diff": "^5.1.0",
-        "minimatch": "^5.0.1",
-        "npm-package-arg": "^9.0.1",
-        "pacote": "^13.6.1",
-        "tar": "^6.1.0"
+        "@npmcli/arborist": "^9.8.0",
+        "@npmcli/installed-package-contents": "^4.0.0",
+        "binary-extensions": "^3.0.0",
+        "diff": "^8.0.2",
+        "minimatch": "^10.0.3",
+        "npm-package-arg": "^13.0.0",
+        "pacote": "^21.0.2",
+        "tar": "^7.5.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmexec": {
-      "version": "4.0.14",
+      "version": "10.3.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/arborist": "^5.6.3",
-        "@npmcli/ci-detect": "^2.0.0",
-        "@npmcli/fs": "^2.1.1",
-        "@npmcli/run-script": "^4.2.0",
-        "chalk": "^4.1.0",
-        "mkdirp-infer-owner": "^2.0.0",
-        "npm-package-arg": "^9.0.1",
-        "npmlog": "^6.0.2",
-        "pacote": "^13.6.1",
-        "proc-log": "^2.0.0",
-        "read": "^1.0.7",
-        "read-package-json-fast": "^2.0.2",
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/arborist": "^9.8.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/run-script": "^10.0.0",
+        "ci-info": "^4.0.0",
+        "npm-package-arg": "^13.0.0",
+        "pacote": "^21.0.2",
+        "proc-log": "^6.0.0",
+        "read": "^5.0.1",
         "semver": "^7.3.7",
-        "walk-up-path": "^1.0.0"
+        "signal-exit": "^4.1.0",
+        "walk-up-path": "^4.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmfund": {
-      "version": "3.0.5",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "@npmcli/arborist": "^5.6.3"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/libnpmhook": {
-      "version": "8.0.4",
+      "version": "7.0.24",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "aproba": "^2.0.0",
-        "npm-registry-fetch": "^13.0.0"
+        "@npmcli/arborist": "^9.8.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmorg": {
-      "version": "4.0.4",
+      "version": "8.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
         "aproba": "^2.0.0",
-        "npm-registry-fetch": "^13.0.0"
+        "npm-registry-fetch": "^19.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmpack": {
-      "version": "4.1.3",
+      "version": "9.1.10",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/run-script": "^4.1.3",
-        "npm-package-arg": "^9.0.1",
-        "pacote": "^13.6.1"
+        "@npmcli/arborist": "^9.8.0",
+        "@npmcli/run-script": "^10.0.0",
+        "npm-package-arg": "^13.0.0",
+        "pacote": "^21.0.2"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmpublish": {
-      "version": "6.0.5",
+      "version": "11.2.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "normalize-package-data": "^4.0.0",
-        "npm-package-arg": "^9.0.1",
-        "npm-registry-fetch": "^13.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "ci-info": "^4.0.0",
+        "npm-package-arg": "^13.0.0",
+        "npm-registry-fetch": "^19.0.0",
+        "proc-log": "^6.0.0",
         "semver": "^7.3.7",
-        "ssri": "^9.0.0"
+        "sigstore": "^4.0.0",
+        "ssri": "^13.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmsearch": {
-      "version": "5.0.4",
+      "version": "9.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-registry-fetch": "^13.0.0"
+        "npm-registry-fetch": "^19.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmteam": {
-      "version": "4.0.4",
+      "version": "8.0.2",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
         "aproba": "^2.0.0",
-        "npm-registry-fetch": "^13.0.0"
+        "npm-registry-fetch": "^19.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/libnpmversion": {
-      "version": "3.0.7",
+      "version": "8.0.4",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/git": "^3.0.0",
-        "@npmcli/run-script": "^4.1.3",
-        "json-parse-even-better-errors": "^2.3.1",
-        "proc-log": "^2.0.0",
+        "@npmcli/git": "^7.0.0",
+        "@npmcli/run-script": "^10.0.0",
+        "json-parse-even-better-errors": "^5.0.0",
+        "proc-log": "^6.0.0",
         "semver": "^7.3.7"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/lru-cache": {
-      "version": "7.13.2",
+      "version": "11.5.1",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "engines": {
-        "node": ">=12"
+        "node": "20 || >=22"
       }
     },
     "node_modules/npm/node_modules/make-fetch-happen": {
-      "version": "10.2.1",
+      "version": "15.0.6",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "agentkeepalive": "^4.2.1",
-        "cacache": "^16.1.0",
-        "http-cache-semantics": "^4.1.0",
-        "http-proxy-agent": "^5.0.0",
-        "https-proxy-agent": "^5.0.0",
-        "is-lambda": "^1.0.1",
-        "lru-cache": "^7.7.1",
-        "minipass": "^3.1.6",
-        "minipass-collect": "^1.0.2",
-        "minipass-fetch": "^2.0.3",
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/agent": "^4.0.0",
+        "@npmcli/redact": "^4.0.0",
+        "cacache": "^20.0.1",
+        "http-cache-semantics": "^4.1.1",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^5.0.0",
         "minipass-flush": "^1.0.5",
         "minipass-pipeline": "^1.2.4",
-        "negotiator": "^0.6.3",
-        "promise-retry": "^2.0.1",
-        "socks-proxy-agent": "^7.0.0",
-        "ssri": "^9.0.0"
+        "negotiator": "^1.0.0",
+        "proc-log": "^6.0.0",
+        "ssri": "^13.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/minimatch": {
-      "version": "5.1.0",
+      "version": "10.2.5",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^2.0.1"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": ">=10"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/npm/node_modules/minipass": {
-      "version": "3.3.4",
+      "version": "7.1.3",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
+      "license": "BlueOak-1.0.0",
       "engines": {
-        "node": ">=8"
+        "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/npm/node_modules/minipass-collect": {
-      "version": "1.0.2",
+      "version": "2.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "minipass": "^3.0.0"
+        "minipass": "^7.0.3"
       },
       "engines": {
-        "node": ">= 8"
+        "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/npm/node_modules/minipass-fetch": {
-      "version": "2.1.1",
+      "version": "5.0.2",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "minipass": "^3.1.6",
-        "minipass-sized": "^1.0.3",
-        "minizlib": "^2.1.2"
+        "minipass": "^7.0.3",
+        "minipass-sized": "^2.0.0",
+        "minizlib": "^3.0.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       },
       "optionalDependencies": {
-        "encoding": "^0.1.13"
+        "iconv-lite": "^0.7.2"
       }
     },
     "node_modules/npm/node_modules/minipass-flush": {
-      "version": "1.0.5",
+      "version": "1.0.6",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "minipass": "^3.0.0"
+        "minipass": "^7.1.3"
       },
       "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/npm/node_modules/minipass-json-stream": {
-      "version": "1.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "jsonparse": "^1.3.1",
-        "minipass": "^3.0.0"
+        "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/npm/node_modules/minipass-pipeline": {
@@ -20619,224 +24343,134 @@
         "node": ">=8"
       }
     },
-    "node_modules/npm/node_modules/minipass-sized": {
-      "version": "1.0.3",
+    "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": {
+      "version": "3.3.6",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "minipass": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/npm/node_modules/minizlib": {
-      "version": "2.1.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "minipass": "^3.0.0",
         "yallist": "^4.0.0"
       },
       "engines": {
-        "node": ">= 8"
+        "node": ">=8"
       }
     },
-    "node_modules/npm/node_modules/mkdirp": {
-      "version": "1.0.4",
+    "node_modules/npm/node_modules/minipass-pipeline/node_modules/yallist": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
+      "license": "ISC"
     },
-    "node_modules/npm/node_modules/mkdirp-infer-owner": {
+    "node_modules/npm/node_modules/minipass-sized": {
       "version": "2.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "chownr": "^2.0.0",
-        "infer-owner": "^1.0.4",
-        "mkdirp": "^1.0.3"
+        "minipass": "^7.1.2"
       },
       "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/npm/node_modules/ms": {
-      "version": "2.1.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/mute-stream": {
-      "version": "0.0.8",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/negotiator": {
-      "version": "0.6.3",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
+        "node": ">=8"
       }
     },
-    "node_modules/npm/node_modules/node-gyp": {
-      "version": "9.1.0",
+    "node_modules/npm/node_modules/minizlib": {
+      "version": "3.1.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "env-paths": "^2.2.0",
-        "glob": "^7.1.4",
-        "graceful-fs": "^4.2.6",
-        "make-fetch-happen": "^10.0.3",
-        "nopt": "^5.0.0",
-        "npmlog": "^6.0.0",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.5",
-        "tar": "^6.1.2",
-        "which": "^2.0.2"
-      },
-      "bin": {
-        "node-gyp": "bin/node-gyp.js"
+        "minipass": "^7.1.2"
       },
       "engines": {
-        "node": "^12.22 || ^14.13 || >=16"
+        "node": ">= 18"
       }
     },
-    "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": {
-      "version": "1.1.11",
+    "node_modules/npm/node_modules/ms": {
+      "version": "2.1.3",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/node-gyp/node_modules/glob": {
-      "version": "7.2.3",
+    "node_modules/npm/node_modules/mute-stream": {
+      "version": "3.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
       "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": {
-      "version": "3.1.2",
+    "node_modules/npm/node_modules/negotiator": {
+      "version": "1.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
+      "license": "MIT",
       "engines": {
-        "node": "*"
+        "node": ">= 0.6"
       }
     },
-    "node_modules/npm/node_modules/node-gyp/node_modules/nopt": {
-      "version": "5.0.0",
+    "node_modules/npm/node_modules/node-gyp": {
+      "version": "12.4.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "abbrev": "1"
+        "env-paths": "^2.2.0",
+        "exponential-backoff": "^3.1.1",
+        "graceful-fs": "^4.2.6",
+        "nopt": "^9.0.0",
+        "proc-log": "^6.0.0",
+        "semver": "^7.3.5",
+        "tar": "^7.5.4",
+        "tinyglobby": "^0.2.12",
+        "undici": "^6.25.0",
+        "which": "^6.0.0"
       },
       "bin": {
-        "nopt": "bin/nopt.js"
+        "node-gyp": "bin/node-gyp.js"
       },
       "engines": {
-        "node": ">=6"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/nopt": {
-      "version": "6.0.0",
+      "version": "9.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "abbrev": "^1.0.0"
+        "abbrev": "^4.0.0"
       },
       "bin": {
         "nopt": "bin/nopt.js"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/normalize-package-data": {
-      "version": "4.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "hosted-git-info": "^5.0.0",
-        "is-core-module": "^2.8.1",
-        "semver": "^7.3.5",
-        "validate-npm-package-license": "^3.0.4"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-audit-report": {
-      "version": "3.0.0",
+      "version": "7.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
-      "dependencies": {
-        "chalk": "^4.0.0"
-      },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-bundled": {
-      "version": "2.0.1",
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-normalize-package-bin": "^2.0.0"
+        "npm-normalize-package-bin": "^5.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-install-checks": {
-      "version": "5.0.0",
+      "version": "8.0.0",
       "dev": true,
       "inBundle": true,
       "license": "BSD-2-Clause",
@@ -20844,226 +24478,177 @@
         "semver": "^7.1.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-normalize-package-bin": {
-      "version": "1.0.1",
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "ISC",
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
     },
     "node_modules/npm/node_modules/npm-package-arg": {
-      "version": "9.1.0",
+      "version": "13.0.2",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "hosted-git-info": "^5.0.0",
-        "proc-log": "^2.0.1",
+        "hosted-git-info": "^9.0.0",
+        "proc-log": "^6.0.0",
         "semver": "^7.3.5",
-        "validate-npm-package-name": "^4.0.0"
+        "validate-npm-package-name": "^7.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-packlist": {
-      "version": "5.1.3",
+      "version": "10.0.4",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "glob": "^8.0.1",
-        "ignore-walk": "^5.0.1",
-        "npm-bundled": "^2.0.0",
-        "npm-normalize-package-bin": "^2.0.0"
-      },
-      "bin": {
-        "npm-packlist": "bin/index.js"
+        "ignore-walk": "^8.0.0",
+        "proc-log": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-pick-manifest": {
-      "version": "7.0.2",
+      "version": "11.0.3",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-install-checks": "^5.0.0",
-        "npm-normalize-package-bin": "^2.0.0",
-        "npm-package-arg": "^9.0.0",
+        "npm-install-checks": "^8.0.0",
+        "npm-normalize-package-bin": "^5.0.0",
+        "npm-package-arg": "^13.0.0",
         "semver": "^7.3.5"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-profile": {
-      "version": "6.2.1",
+      "version": "12.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "npm-registry-fetch": "^13.0.1",
-        "proc-log": "^2.0.0"
+        "npm-registry-fetch": "^19.0.0",
+        "proc-log": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-registry-fetch": {
-      "version": "13.3.1",
+      "version": "19.1.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "make-fetch-happen": "^10.0.6",
-        "minipass": "^3.1.6",
-        "minipass-fetch": "^2.0.3",
-        "minipass-json-stream": "^1.0.1",
-        "minizlib": "^2.1.2",
-        "npm-package-arg": "^9.0.1",
-        "proc-log": "^2.0.0"
+        "@npmcli/redact": "^4.0.0",
+        "jsonparse": "^1.3.1",
+        "make-fetch-happen": "^15.0.0",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^5.0.0",
+        "minizlib": "^3.0.1",
+        "npm-package-arg": "^13.0.0",
+        "proc-log": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/npm-user-validate": {
-      "version": "1.0.1",
-      "dev": true,
-      "inBundle": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/npm/node_modules/npmlog": {
-      "version": "6.0.2",
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "are-we-there-yet": "^3.0.0",
-        "console-control-strings": "^1.1.0",
-        "gauge": "^4.0.3",
-        "set-blocking": "^2.0.0"
-      },
+      "license": "BSD-2-Clause",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/npm/node_modules/once": {
-      "version": "1.4.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/npm/node_modules/opener": {
-      "version": "1.5.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "(WTFPL OR MIT)",
-      "bin": {
-        "opener": "bin/opener-bin.js"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/p-map": {
-      "version": "4.0.0",
+      "version": "7.0.4",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
-      "dependencies": {
-        "aggregate-error": "^3.0.0"
-      },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/npm/node_modules/pacote": {
-      "version": "13.6.2",
+      "version": "21.5.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "@npmcli/git": "^3.0.0",
-        "@npmcli/installed-package-contents": "^1.0.7",
-        "@npmcli/promise-spawn": "^3.0.0",
-        "@npmcli/run-script": "^4.1.0",
-        "cacache": "^16.0.0",
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.1.0",
-        "infer-owner": "^1.0.4",
-        "minipass": "^3.1.6",
-        "mkdirp": "^1.0.4",
-        "npm-package-arg": "^9.0.0",
-        "npm-packlist": "^5.1.0",
-        "npm-pick-manifest": "^7.0.0",
-        "npm-registry-fetch": "^13.0.1",
-        "proc-log": "^2.0.0",
-        "promise-retry": "^2.0.1",
-        "read-package-json": "^5.0.0",
-        "read-package-json-fast": "^2.0.3",
-        "rimraf": "^3.0.2",
-        "ssri": "^9.0.0",
-        "tar": "^6.1.11"
+        "@gar/promise-retry": "^1.0.0",
+        "@npmcli/git": "^7.0.0",
+        "@npmcli/installed-package-contents": "^4.0.0",
+        "@npmcli/package-json": "^7.0.0",
+        "@npmcli/promise-spawn": "^9.0.0",
+        "@npmcli/run-script": "^10.0.0",
+        "cacache": "^20.0.0",
+        "fs-minipass": "^3.0.0",
+        "minipass": "^7.0.2",
+        "npm-package-arg": "^13.0.0",
+        "npm-packlist": "^10.0.1",
+        "npm-pick-manifest": "^11.0.1",
+        "npm-registry-fetch": "^19.0.0",
+        "proc-log": "^6.0.0",
+        "sigstore": "^4.0.0",
+        "ssri": "^13.0.0",
+        "tar": "^7.4.3"
       },
       "bin": {
-        "pacote": "lib/bin.js"
+        "pacote": "bin/index.js"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/parse-conflict-json": {
-      "version": "2.0.2",
+      "version": "5.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "json-parse-even-better-errors": "^2.3.1",
-        "just-diff": "^5.0.1",
+        "json-parse-even-better-errors": "^5.0.0",
+        "just-diff": "^6.0.0",
         "just-diff-apply": "^5.2.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/path-is-absolute": {
-      "version": "1.0.1",
+    "node_modules/npm/node_modules/path-scurry": {
+      "version": "2.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^11.0.0",
+        "minipass": "^7.1.2"
+      },
       "engines": {
-        "node": ">=0.10.0"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/npm/node_modules/postcss-selector-parser": {
-      "version": "6.0.10",
+      "version": "7.1.4",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
@@ -21076,12 +24661,21 @@
       }
     },
     "node_modules/npm/node_modules/proc-log": {
-      "version": "2.0.1",
+      "version": "6.1.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/npm/node_modules/proggy": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
     "node_modules/npm/node_modules/promise-all-reject-late": {
@@ -21094,7 +24688,7 @@
       }
     },
     "node_modules/npm/node_modules/promise-call-limit": {
-      "version": "1.0.1",
+      "version": "3.0.2",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
@@ -21102,775 +24696,880 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/npm/node_modules/promise-inflight": {
-      "version": "1.0.1",
+    "node_modules/npm/node_modules/promzard": {
+      "version": "3.0.1",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "ISC",
+      "dependencies": {
+        "read": "^5.0.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
     },
-    "node_modules/npm/node_modules/promise-retry": {
-      "version": "2.0.1",
+    "node_modules/npm/node_modules/qrcode-terminal": {
+      "version": "0.12.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "bin": {
+        "qrcode-terminal": "bin/qrcode-terminal.js"
+      }
+    },
+    "node_modules/npm/node_modules/read": {
+      "version": "5.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
       "dependencies": {
-        "err-code": "^2.0.2",
-        "retry": "^0.12.0"
+        "mute-stream": "^3.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/promzard": {
-      "version": "0.3.0",
+    "node_modules/npm/node_modules/read-cmd-shim": {
+      "version": "6.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
-      "dependencies": {
-        "read": "1"
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/qrcode-terminal": {
-      "version": "0.12.0",
+    "node_modules/npm/node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/npm/node_modules/semver": {
+      "version": "7.8.4",
       "dev": true,
       "inBundle": true,
+      "license": "ISC",
       "bin": {
-        "qrcode-terminal": "bin/qrcode-terminal.js"
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
       }
     },
-    "node_modules/npm/node_modules/read": {
-      "version": "1.0.7",
+    "node_modules/npm/node_modules/signal-exit": {
+      "version": "4.1.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/npm/node_modules/sigstore": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "mute-stream": "~0.0.4"
+        "@sigstore/bundle": "^4.0.0",
+        "@sigstore/core": "^3.2.1",
+        "@sigstore/protobuf-specs": "^0.5.0",
+        "@sigstore/sign": "^4.1.1",
+        "@sigstore/tuf": "^4.0.2",
+        "@sigstore/verify": "^3.1.1"
       },
       "engines": {
-        "node": ">=0.8"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/read-cmd-shim": {
-      "version": "3.0.0",
+    "node_modules/npm/node_modules/smart-buffer": {
+      "version": "4.2.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
       }
     },
-    "node_modules/npm/node_modules/read-package-json": {
-      "version": "5.0.2",
+    "node_modules/npm/node_modules/socks": {
+      "version": "2.8.9",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ip-address": "^10.1.1",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/npm/node_modules/socks-proxy-agent": {
+      "version": "8.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "socks": "^2.8.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/npm/node_modules/spdx-exceptions": {
+      "version": "2.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC-BY-3.0"
+    },
+    "node_modules/npm/node_modules/spdx-expression-parse": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/npm/node_modules/spdx-license-ids": {
+      "version": "3.0.23",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^8.0.1",
-        "json-parse-even-better-errors": "^2.3.1",
-        "normalize-package-data": "^4.0.0",
-        "npm-normalize-package-bin": "^2.0.0"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
+      "license": "CC0-1.0"
     },
-    "node_modules/npm/node_modules/read-package-json-fast": {
-      "version": "2.0.3",
+    "node_modules/npm/node_modules/ssri": {
+      "version": "13.0.1",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
       "dependencies": {
-        "json-parse-even-better-errors": "^2.3.0",
-        "npm-normalize-package-bin": "^1.0.1"
+        "minipass": "^7.0.3"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": {
-      "version": "2.0.0",
+    "node_modules/npm/node_modules/supports-color": {
+      "version": "10.2.2",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
       }
     },
-    "node_modules/npm/node_modules/readable-stream": {
-      "version": "3.6.0",
+    "node_modules/npm/node_modules/tar": {
+      "version": "7.5.16",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
+        "@isaacs/fs-minipass": "^4.0.0",
+        "chownr": "^3.0.0",
+        "minipass": "^7.1.2",
+        "minizlib": "^3.1.0",
+        "yallist": "^5.0.0"
       },
       "engines": {
-        "node": ">= 6"
+        "node": ">=18"
       }
     },
-    "node_modules/npm/node_modules/readdir-scoped-modules": {
-      "version": "1.1.0",
+    "node_modules/npm/node_modules/text-table": {
+      "version": "0.2.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "debuglog": "^1.0.1",
-        "dezalgo": "^1.0.0",
-        "graceful-fs": "^4.1.2",
-        "once": "^1.3.0"
-      }
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/retry": {
-      "version": "0.12.0",
+    "node_modules/npm/node_modules/tiny-relative-date": {
+      "version": "2.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/rimraf": {
-      "version": "3.0.2",
+    "node_modules/npm/node_modules/tinyglobby": {
+      "version": "0.2.17",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "glob": "^7.1.3"
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
       },
-      "bin": {
-        "rimraf": "bin.js"
+      "engines": {
+        "node": ">=12.0.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "url": "https://github.com/sponsors/SuperchupuDev"
       }
     },
-    "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": {
-      "version": "1.1.11",
+    "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": {
+      "version": "6.5.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
       }
     },
-    "node_modules/npm/node_modules/rimraf/node_modules/glob": {
-      "version": "7.2.3",
+    "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": {
+      "version": "4.0.4",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
+      "license": "MIT",
       "engines": {
-        "node": "*"
+        "node": ">=12"
       },
       "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
-    "node_modules/npm/node_modules/rimraf/node_modules/minimatch": {
-      "version": "3.1.2",
+    "node_modules/npm/node_modules/treeverse": {
+      "version": "3.0.0",
       "dev": true,
       "inBundle": true,
       "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
       "engines": {
-        "node": "*"
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
-    "node_modules/npm/node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/safer-buffer": {
-      "version": "2.1.2",
+    "node_modules/npm/node_modules/tuf-js": {
+      "version": "4.1.0",
       "dev": true,
       "inBundle": true,
       "license": "MIT",
-      "optional": true
-    },
-    "node_modules/npm/node_modules/semver": {
-      "version": "7.3.7",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
       "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
-      "bin": {
-        "semver": "bin/semver.js"
+        "@tufjs/models": "4.1.0",
+        "debug": "^4.4.3",
+        "make-fetch-happen": "^15.0.1"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/semver/node_modules/lru-cache": {
-      "version": "6.0.0",
+    "node_modules/npm/node_modules/undici": {
+      "version": "6.26.0",
       "dev": true,
       "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
+      "license": "MIT",
       "engines": {
-        "node": ">=10"
+        "node": ">=18.17"
       }
     },
-    "node_modules/npm/node_modules/set-blocking": {
-      "version": "2.0.0",
+    "node_modules/npm/node_modules/util-deprecate": {
+      "version": "1.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/signal-exit": {
-      "version": "3.0.7",
+    "node_modules/npm/node_modules/validate-npm-package-name": {
+      "version": "7.0.2",
       "dev": true,
       "inBundle": true,
-      "license": "ISC"
+      "license": "ISC",
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
     },
-    "node_modules/npm/node_modules/smart-buffer": {
-      "version": "4.2.0",
+    "node_modules/npm/node_modules/walk-up-path": {
+      "version": "4.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "engines": {
-        "node": ">= 6.0.0",
-        "npm": ">= 3.0.0"
+        "node": "20 || >=22"
       }
     },
-    "node_modules/npm/node_modules/socks": {
-      "version": "2.7.0",
+    "node_modules/npm/node_modules/which": {
+      "version": "6.0.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "ip": "^2.0.0",
-        "smart-buffer": "^4.2.0"
+        "isexe": "^4.0.0"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
       },
       "engines": {
-        "node": ">= 10.13.0",
-        "npm": ">= 3.0.0"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/socks-proxy-agent": {
-      "version": "7.0.0",
+    "node_modules/npm/node_modules/write-file-atomic": {
+      "version": "7.0.1",
       "dev": true,
       "inBundle": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "agent-base": "^6.0.2",
-        "debug": "^4.3.3",
-        "socks": "^2.6.2"
+        "signal-exit": "^4.0.1"
       },
       "engines": {
-        "node": ">= 10"
+        "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/npm/node_modules/spdx-correct": {
-      "version": "3.1.1",
+    "node_modules/npm/node_modules/yallist": {
+      "version": "5.0.0",
       "dev": true,
       "inBundle": true,
-      "license": "Apache-2.0",
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
       "dependencies": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
       }
     },
-    "node_modules/npm/node_modules/spdx-exceptions": {
-      "version": "2.3.0",
+    "node_modules/nwsapi": {
+      "version": "2.2.24",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz",
+      "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==",
       "dev": true,
-      "inBundle": true,
-      "license": "CC-BY-3.0"
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/spdx-expression-parse": {
-      "version": "3.0.1",
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
-      "dependencies": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/spdx-license-ids": {
-      "version": "3.0.11",
+    "node_modules/object-filter": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
+      "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==",
       "dev": true,
-      "inBundle": true,
-      "license": "CC0-1.0"
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/ssri": {
-      "version": "9.0.1",
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.assign": {
+      "version": "4.1.7",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+      "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+      "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "minipass": "^3.1.1"
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0",
+        "has-symbols": "^1.1.0",
+        "object-keys": "^1.1.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/npm/node_modules/string_decoder": {
-      "version": "1.3.0",
+    "node_modules/object.defaults": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+      "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "safe-buffer": "~5.2.0"
+        "array-each": "^1.0.1",
+        "array-slice": "^1.0.0",
+        "for-own": "^1.0.0",
+        "isobject": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/string-width": {
-      "version": "4.2.3",
+    "node_modules/object.defaults/node_modules/for-own": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
+        "for-in": "^1.0.1"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/strip-ansi": {
-      "version": "6.0.1",
+    "node_modules/object.entries": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+      "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "ansi-regex": "^5.0.1"
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.1.1"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">= 0.4"
       }
     },
-    "node_modules/npm/node_modules/supports-color": {
-      "version": "7.2.0",
+    "node_modules/object.fromentries": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "has-flag": "^4.0.0"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-object-atoms": "^1.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/npm/node_modules/tar": {
-      "version": "6.1.11",
+    "node_modules/object.groupby": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.0.0",
-        "minipass": "^3.0.0",
-        "minizlib": "^2.1.1",
-        "mkdirp": "^1.0.3",
-        "yallist": "^4.0.0"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2"
       },
       "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/npm/node_modules/text-table": {
-      "version": "0.2.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/tiny-relative-date": {
-      "version": "1.3.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/treeverse": {
-      "version": "2.0.0",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">= 0.4"
       }
     },
-    "node_modules/npm/node_modules/unique-filename": {
-      "version": "2.0.1",
+    "node_modules/object.map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+      "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "unique-slug": "^3.0.0"
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/unique-slug": {
-      "version": "3.0.0",
+    "node_modules/object.map/node_modules/for-own": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "imurmurhash": "^0.1.4"
+        "for-in": "^1.0.1"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "MIT"
-    },
-    "node_modules/npm/node_modules/validate-npm-package-license": {
-      "version": "3.0.4",
+    "node_modules/object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
       "dev": true,
-      "inBundle": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
       "dependencies": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/npm/node_modules/validate-npm-package-name": {
-      "version": "4.0.0",
+    "node_modules/object.values": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+      "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "builtins": "^5.0.0"
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.3",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/npm/node_modules/walk-up-path": {
-      "version": "1.0.0",
+    "node_modules/obuf": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC"
+      "license": "MIT"
     },
-    "node_modules/npm/node_modules/wcwidth": {
-      "version": "1.0.1",
+    "node_modules/octokit": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz",
+      "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==",
       "dev": true,
-      "inBundle": true,
       "license": "MIT",
       "dependencies": {
-        "defaults": "^1.0.3"
-      }
-    },
-    "node_modules/npm/node_modules/which": {
-      "version": "2.0.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
+        "@octokit/app": "^14.0.2",
+        "@octokit/core": "^5.0.0",
+        "@octokit/oauth-app": "^6.0.0",
+        "@octokit/plugin-paginate-graphql": "^4.0.0",
+        "@octokit/plugin-paginate-rest": "^9.0.0",
+        "@octokit/plugin-rest-endpoint-methods": "^10.0.0",
+        "@octokit/plugin-retry": "^6.0.0",
+        "@octokit/plugin-throttling": "^8.0.0",
+        "@octokit/request-error": "^5.0.0",
+        "@octokit/types": "^12.0.0"
       },
       "engines": {
-        "node": ">= 8"
+        "node": ">= 18"
       }
     },
-    "node_modules/npm/node_modules/wide-align": {
-      "version": "1.1.5",
+    "node_modules/octokit/node_modules/@octokit/auth-token": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+      "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^1.0.2 || 2 || 3 || 4"
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/npm/node_modules/wrappy": {
-      "version": "1.0.2",
-      "dev": true,
-      "inBundle": true,
-      "license": "ISC"
-    },
-    "node_modules/npm/node_modules/write-file-atomic": {
-      "version": "4.0.2",
+    "node_modules/octokit/node_modules/@octokit/core": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
+      "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "imurmurhash": "^0.1.4",
-        "signal-exit": "^3.0.7"
+        "@octokit/auth-token": "^4.0.0",
+        "@octokit/graphql": "^7.1.0",
+        "@octokit/request": "^8.4.1",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.0.0",
+        "before-after-hook": "^2.2.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/npm/node_modules/yallist": {
-      "version": "4.0.0",
+    "node_modules/octokit/node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
-      "inBundle": true,
-      "license": "ISC"
+      "license": "MIT"
     },
-    "node_modules/nth-check": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
-      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+    "node_modules/octokit/node_modules/@octokit/core/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
-      "license": "BSD-2-Clause",
+      "license": "MIT",
       "dependencies": {
-        "boolbase": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/nth-check?sponsor=1"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/nwsapi": {
-      "version": "2.2.10",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz",
-      "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+    "node_modules/octokit/node_modules/@octokit/endpoint": {
+      "version": "9.0.6",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
+      "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
+      },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/object-filter": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
-      "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==",
+    "node_modules/octokit/node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/object-hash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
-      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+    "node_modules/octokit/node_modules/@octokit/endpoint/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
+      "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">= 6"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/object-inspect": {
-      "version": "1.13.4",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
-      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+    "node_modules/octokit/node_modules/@octokit/graphql": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
+      "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
+      "dependencies": {
+        "@octokit/request": "^8.4.1",
+        "@octokit/types": "^13.0.0",
+        "universal-user-agent": "^6.0.0"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "engines": {
+        "node": ">= 18"
       }
     },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+    "node_modules/octokit/node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/octokit/node_modules/@octokit/graphql/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
+      "dependencies": {
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/object.assign": {
-      "version": "4.1.7",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
-      "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+    "node_modules/octokit/node_modules/@octokit/openapi-types": {
+      "version": "20.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
+      "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/octokit/node_modules/@octokit/plugin-paginate-rest": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
+      "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0",
-        "has-symbols": "^1.1.0",
-        "object-keys": "^1.1.1"
+        "@octokit/types": "^12.6.0"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">= 18"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "peerDependencies": {
+        "@octokit/core": "5"
       }
     },
-    "node_modules/object.defaults": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
-      "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
+    "node_modules/octokit/node_modules/@octokit/plugin-rest-endpoint-methods": {
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
+      "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "array-each": "^1.0.1",
-        "array-slice": "^1.0.0",
-        "for-own": "^1.0.0",
-        "isobject": "^3.0.0"
+        "@octokit/types": "^12.6.0"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@octokit/core": "5"
       }
     },
-    "node_modules/object.defaults/node_modules/for-own": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-      "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
+    "node_modules/octokit/node_modules/@octokit/plugin-retry": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz",
+      "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "for-in": "^1.0.1"
+        "@octokit/request-error": "^5.0.0",
+        "@octokit/types": "^13.0.0",
+        "bottleneck": "^2.15.3"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "@octokit/core": "5"
       }
     },
-    "node_modules/object.entries": {
-      "version": "1.1.9",
-      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
-      "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+    "node_modules/octokit/node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/octokit/node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.4",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/object.fromentries": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
-      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+    "node_modules/octokit/node_modules/@octokit/plugin-throttling": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz",
+      "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0"
+        "@octokit/types": "^12.2.0",
+        "bottleneck": "^2.15.3"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">= 18"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "peerDependencies": {
+        "@octokit/core": "^5.0.0"
       }
     },
-    "node_modules/object.groupby": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
-      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+    "node_modules/octokit/node_modules/@octokit/request": {
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
+      "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2"
+        "@octokit/endpoint": "^9.0.6",
+        "@octokit/request-error": "^5.1.1",
+        "@octokit/types": "^13.1.0",
+        "universal-user-agent": "^6.0.0"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">= 18"
       }
     },
-    "node_modules/object.map": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
-      "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
+    "node_modules/octokit/node_modules/@octokit/request-error": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
+      "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "for-own": "^1.0.0",
-        "make-iterator": "^1.0.0"
+        "@octokit/types": "^13.1.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 18"
       }
     },
-    "node_modules/object.map/node_modules/for-own": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-      "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
+    "node_modules/octokit/node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/octokit/node_modules/@octokit/request-error/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "for-in": "^1.0.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+    "node_modules/octokit/node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
+      "version": "24.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
+      "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/octokit/node_modules/@octokit/request/node_modules/@octokit/types": {
+      "version": "13.10.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
+      "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "isobject": "^3.0.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
+        "@octokit/openapi-types": "^24.2.0"
       }
     },
-    "node_modules/object.values": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
-      "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+    "node_modules/octokit/node_modules/@octokit/types": {
+      "version": "12.6.0",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+      "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "@octokit/openapi-types": "^20.0.0"
       }
     },
-    "node_modules/obuf": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
-      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+    "node_modules/octokit/node_modules/before-after-hook": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
       "dev": true,
-      "license": "MIT"
+      "license": "Apache-2.0"
+    },
+    "node_modules/octokit/node_modules/universal-user-agent": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "dev": true,
+      "license": "ISC"
     },
     "node_modules/on-finished": {
       "version": "2.4.1",
@@ -22163,39 +25862,48 @@
       }
     },
     "node_modules/p-each-series": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
-      "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz",
+      "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">=12"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/p-filter": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz",
-      "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==",
+    "node_modules/p-event": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz",
+      "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "p-map": "^2.0.0"
+        "p-timeout": "^6.1.2"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=16.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/p-is-promise": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz",
-      "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==",
+    "node_modules/p-filter": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz",
+      "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "p-map": "^7.0.1"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/p-limit": {
@@ -22244,13 +25952,16 @@
       }
     },
     "node_modules/p-map": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
-      "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
+      "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=6"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/p-reduce": {
@@ -22277,6 +25988,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/p-timeout": {
+      "version": "6.1.4",
+      "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
+      "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -22307,44 +26031,6 @@
         "node": ">= 14"
       }
     },
-    "node_modules/pac-proxy-agent/node_modules/agent-base": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
-      "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "agent-base": "^7.1.0",
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
-      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "agent-base": "^7.1.2",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
     "node_modules/pac-resolver": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
@@ -22366,6 +26052,13 @@
       "dev": true,
       "license": "BlueOak-1.0.0"
     },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "dev": true,
+      "license": "(MIT AND Zlib)"
+    },
     "node_modules/param-case": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
@@ -22429,6 +26122,16 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/parse-imports-exports": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz",
+      "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parse-statements": "1.0.11"
+      }
+    },
     "node_modules/parse-json": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -22448,6 +26151,19 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/parse-ms": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+      "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/parse-passwd": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
@@ -22458,17 +26174,54 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/parse5": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
-      "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+    "node_modules/parse-statements": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz",
+      "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parse5": "^6.0.1"
+      }
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+      "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/parse5/node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
       "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "entities": "^4.4.0"
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
       },
       "funding": {
-        "url": "https://github.com/inikulin/parse5?sponsor=1"
+        "url": "https://github.com/fb55/entities?sponsor=1"
       }
     },
     "node_modules/parseurl": {
@@ -22511,6 +26264,22 @@
         "node": ">=8"
       }
     },
+    "node_modules/path-expression-matcher": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.1.tgz",
+      "integrity": "sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -22521,13 +26290,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/path-is-inside": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-      "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
-      "dev": true,
-      "license": "(WTFPL OR MIT)"
-    },
     "node_modules/path-key": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -22616,6 +26378,40 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/pg-int8": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+      "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/pg-protocol": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz",
+      "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pg-types": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+      "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pg-int8": "1.0.1",
+        "postgres-array": "~2.0.0",
+        "postgres-bytea": "~1.0.0",
+        "postgres-date": "~1.0.4",
+        "postgres-interval": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -22650,42 +26446,19 @@
       }
     },
     "node_modules/pify": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "pinkie": "^2.0.0"
-      },
       "engines": {
         "node": ">=0.10.0"
       }
     },
     "node_modules/pirates": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
-      "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+      "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -22793,12 +26566,13 @@
       }
     },
     "node_modules/playwright": {
-      "version": "1.53.2",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
-      "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
+      "version": "1.61.1",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
+      "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "playwright-core": "1.53.2"
+        "playwright-core": "1.61.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -22811,10 +26585,11 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.53.2",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
-      "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
+      "version": "1.61.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
+      "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
       "dev": true,
+      "license": "Apache-2.0",
       "bin": {
         "playwright-core": "cli.js"
       },
@@ -22828,6 +26603,7 @@
       "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
       "dev": true,
       "hasInstallScript": true,
+      "license": "MIT",
       "optional": true,
       "os": [
         "darwin"
@@ -22863,9 +26639,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.5.6",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
-      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "version": "8.5.16",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
+      "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
       "dev": true,
       "funding": [
         {
@@ -22883,7 +26659,7 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "nanoid": "^3.3.11",
+        "nanoid": "^3.3.12",
         "picocolors": "^1.1.1",
         "source-map-js": "^1.2.1"
       },
@@ -22909,9 +26685,9 @@
       }
     },
     "node_modules/postcss-calc/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -22923,61 +26699,72 @@
       }
     },
     "node_modules/postcss-colormin": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.4.tgz",
-      "integrity": "sha512-ziQuVzQZBROpKpfeDwmrG+Vvlr0YWmY/ZAk99XD+mGEBuEojoFekL41NCsdhyNUtZI7DPOoIWIR7vQQK9xwluw==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-8.0.1.tgz",
+      "integrity": "sha512-qBY4ABQ6d8/mk5RRZHwMllrZMxeMey3azVY2dZUEk+RgiUC4ARdPR3/AITzNqqKTbvW/3y/MJKinDrzwqn8RDQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "caniuse-api": "^3.0.0",
-        "colord": "^2.9.3",
+        "@colordx/core": "^5.4.3",
+        "browserslist": "^4.28.2",
+        "caniuse-api": "^4.0.0",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
+      }
+    },
+    "node_modules/postcss-colormin/node_modules/caniuse-api": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-4.0.0.tgz",
+      "integrity": "sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0"
       }
     },
     "node_modules/postcss-convert-values": {
-      "version": "7.0.7",
-      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.7.tgz",
-      "integrity": "sha512-HR9DZLN04Xbe6xugRH6lS4ZQH2zm/bFh/ZyRkpedZozhvh+awAfbA0P36InO4fZfDhvYfNJeNvlTf1sjwGbw/A==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-8.0.1.tgz",
+      "integrity": "sha512-IdOSIX3BzfMvCc1TAHIha2gfy17xnb5vfML8e2BIKARnFOghksESfaSAB/3CXgyLfMozZAbTRPVQF5dbuKOidw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
+        "browserslist": "^4.28.2",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-discard-comments": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.4.tgz",
-      "integrity": "sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-8.0.1.tgz",
+      "integrity": "sha512-FDvzm3tXlEsQBO2XQgnta5ugsAqwBrgWH+j5QgXpegEIDYA0VPnZg2aP7LtmWtC49POskeIhXesFiU/k3NyFHA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "postcss-selector-parser": "^7.1.0"
+        "postcss-selector-parser": "^7.1.2"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-discard-comments/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -22989,42 +26776,60 @@
       }
     },
     "node_modules/postcss-discard-duplicates": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz",
-      "integrity": "sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-8.0.1.tgz",
+      "integrity": "sha512-stTDXkI8YkCUfADurQhp03oq5ynsgSx6Qrw5B1swds6oTHtAeOZ9I0SHGK8cY/VpWUsIYFDWMs3IWf9jIEfFvA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-discard-empty": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz",
-      "integrity": "sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-8.0.1.tgz",
+      "integrity": "sha512-Zv4fM1Yfhk71tbt6gfiptbL6jDHi+7apSnaMeaO9n1uET+1embrXQw5m93Zp5x28UyQSuv+AVkFY193jdwZ33w==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-discard-overridden": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz",
-      "integrity": "sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-8.0.1.tgz",
+      "integrity": "sha512-ykt4fvrC7yYGzbxKyqBVjDCbsjF/11JgWK8enrdkobRyqqEtb/uDUCbKOGdvrK8X7BrShW8Lv5cCRNbdkNHGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.5.15"
+      }
+    },
+    "node_modules/postcss-import": {
+      "version": "16.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz",
+      "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "postcss-value-parser": "^4.0.0",
+        "read-cache": "^1.0.0",
+        "resolve": "^1.1.7"
+      },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": ">=18.0.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.0.0"
       }
     },
     "node_modules/postcss-loader": {
@@ -23098,45 +26903,56 @@
       "license": "MIT"
     },
     "node_modules/postcss-merge-longhand": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz",
-      "integrity": "sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-8.0.1.tgz",
+      "integrity": "sha512-huTfSYgQ13O81SFvAuOi7GWnO48vvybjj3xF+X3qUoPjzvvaLpJH5DcUqqXcwOEulZUcvaV4s0V9WtWs+IAQPA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0",
-        "stylehacks": "^7.0.5"
+        "stylehacks": "^8.0.1"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-merge-rules": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.6.tgz",
-      "integrity": "sha512-2jIPT4Tzs8K87tvgCpSukRQ2jjd+hH6Bb8rEEOUDmmhOeTcqDg5fEFK8uKIu+Pvc3//sm3Uu6FRqfyv7YF7+BQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-8.0.1.tgz",
+      "integrity": "sha512-o3rk4UpnPNg469tklYwbR/NtvKc/f/wJiVDTnNQ/EFPw/LeiPOHUCvV1GIBQIZHGrBAYdPjToK6a+ojYprsrxQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "caniuse-api": "^3.0.0",
-        "cssnano-utils": "^5.0.1",
-        "postcss-selector-parser": "^7.1.0"
+        "browserslist": "^4.28.2",
+        "caniuse-api": "^4.0.0",
+        "cssnano-utils": "^6.0.1",
+        "postcss-selector-parser": "^7.1.2"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
+      }
+    },
+    "node_modules/postcss-merge-rules/node_modules/caniuse-api": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-4.0.0.tgz",
+      "integrity": "sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0"
       }
     },
     "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -23148,78 +26964,91 @@
       }
     },
     "node_modules/postcss-minify-font-values": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz",
-      "integrity": "sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-8.0.1.tgz",
+      "integrity": "sha512-L8Nzs/PRlBSPrLdY/7rAiU5ZN5800+2J/4LRbfyG8SJnPljmgMaXVmQiCklvRS+yObfVRNtvmk/Ean/eoYcSeg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-minify-gradients": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz",
-      "integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-8.0.1.tgz",
+      "integrity": "sha512-qf+4s/hZMqTwpWN2teqf6+1yvR/SZK5HgHqXYuACeJXV7ABe7AXtBEomgxagUzcN4bSnmqBh5vnIml0dYqykYg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "colord": "^2.9.3",
-        "cssnano-utils": "^5.0.1",
+        "@colordx/core": "^5.4.3",
+        "cssnano-utils": "^6.0.1",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-minify-params": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.4.tgz",
-      "integrity": "sha512-3OqqUddfH8c2e7M35W6zIwv7jssM/3miF9cbCSb1iJiWvtguQjlxZGIHK9JRmc8XAKmE2PFGtHSM7g/VcW97sw==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-8.0.1.tgz",
+      "integrity": "sha512-L0h3H59deFfFg0wQN1NVaS/8E/LfGvaMuZKGO7siwlG995zo3OshtQyRkqKdVqcBwAORBvZ1nDZrKPLRapYkQw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "cssnano-utils": "^5.0.1",
+        "browserslist": "^4.28.2",
+        "cssnano-utils": "^6.0.1",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-minify-selectors": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz",
-      "integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==",
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-8.0.2.tgz",
+      "integrity": "sha512-3icdxc/zght5UAizdwqZBDE2KOWHf1jMQCxET6iLACeNlRxfTPyXS0/COpGk8CQ2cECyaEKTRUd/i/k8Gxmz4g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
+        "browserslist": "^4.28.1",
+        "caniuse-api": "^4.0.0",
         "cssesc": "^3.0.0",
-        "postcss-selector-parser": "^7.1.0"
+        "postcss-selector-parser": "^7.1.2"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
+      }
+    },
+    "node_modules/postcss-minify-selectors/node_modules/caniuse-api": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-4.0.0.tgz",
+      "integrity": "sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0"
       }
     },
     "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -23294,219 +27123,240 @@
       }
     },
     "node_modules/postcss-normalize-charset": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz",
-      "integrity": "sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-8.0.1.tgz",
+      "integrity": "sha512-xzqr36F8UeIZOvOHsf3aul+RVJCADvSwuwpMLgizqKjisHZpBfztgW0XFLBfJvz9pJgaStaOXAtGb0zLqT6B0w==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-display-values": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz",
-      "integrity": "sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-8.0.1.tgz",
+      "integrity": "sha512-ZDWOijOK1FFMlpgiQCUO9fCNKd7HJ9L7z9HWEq4iyubnUFWzdTSwm/LcrMbNW6iZ1oAtqeLYA0WA3xHszOI08g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-positions": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz",
-      "integrity": "sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-8.0.1.tgz",
+      "integrity": "sha512-uuivan2poSqbE48ST4do20dGaFUeXey9/H8rhHzoyVHB2I6BmkoVLZ/C9+BRjUlpaAFYVOoDY7epkiidzaYbvA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-repeat-style": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz",
-      "integrity": "sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-8.0.1.tgz",
+      "integrity": "sha512-q2hq5fmKxk29K6DjKA3nZ17Q2dtjhLYFNmFweKALmooUqx6UWAHF1bBoWTu/EqlJ88josb82A/J0Atj9LJUmpQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-string": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz",
-      "integrity": "sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-8.0.1.tgz",
+      "integrity": "sha512-+Wf+kQJhm1WgSGEAuUaswE9rdpR9QbrKRVemcVHs6rhOoOTVIdAbgaicftfYA6vLM346P8onRzkEVbFN29ktKQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-timing-functions": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz",
-      "integrity": "sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-8.0.1.tgz",
+      "integrity": "sha512-W8/tvwRlm3T+yjGkg0IRTF4bvHj0vILYr/LOogCrJKHz2ey2HFRwfsAA8Bk9N4BGR7z7WmmDu/KzzwhJ6FoGPQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-unicode": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.4.tgz",
-      "integrity": "sha512-LvIURTi1sQoZqj8mEIE8R15yvM+OhbR1avynMtI9bUzj5gGKR/gfZFd8O7VMj0QgJaIFzxDwxGl/ASMYAkqO8g==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-8.0.1.tgz",
+      "integrity": "sha512-Ad0YHNRBp4WHEOYUM/4wL/8MoL2fimEF8se/0q+Rt/owMzYpbxsypC1P8fN/oluwoRmRKdNVX7X2oycEobPWcQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
+        "browserslist": "^4.28.2",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-url": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz",
-      "integrity": "sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-8.0.1.tgz",
+      "integrity": "sha512-tkYcip6pCDY806xuxpJYqMW2M3/623jzGFJmz3m5Us47q8P28+gbRZxaea3Rr/CmwwLUiVlh+BTGYwQ6gvaP8A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-normalize-whitespace": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz",
-      "integrity": "sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-8.0.1.tgz",
+      "integrity": "sha512-XzORadNfSrKWDZZpgAEHPKINKx8r9r9RIfE9c70g/HThdpbmPHhDYCodHSVESDxmKeySAYw1p4liuBCf7j6LyA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-ordered-values": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz",
-      "integrity": "sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-8.0.1.tgz",
+      "integrity": "sha512-OLXq5lR1yk3KWQ1FPK6aWjFFdktHE9f9kb8cnt4LmIw7w30DnzgD9+sOVYJc5HenkWCX8i1MJhhFwmqc/GYqLg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cssnano-utils": "^5.0.1",
+        "cssnano-utils": "^6.0.1",
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-reduce-initial": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.4.tgz",
-      "integrity": "sha512-rdIC9IlMBn7zJo6puim58Xd++0HdbvHeHaPgXsimMfG1ijC5A9ULvNLSE0rUKVJOvNMcwewW4Ga21ngyJjY/+Q==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-8.0.1.tgz",
+      "integrity": "sha512-+aQsR6+61KRoIfcFNLP3v9RM7+0iYOTtPnjl1wr6JqMW1zx6S+t2ktHRefXwacFdHIDj5+ETG0KY7K3+SGQ4Nw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "caniuse-api": "^3.0.0"
+        "browserslist": "^4.28.2",
+        "caniuse-api": "^4.0.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
+      }
+    },
+    "node_modules/postcss-reduce-initial/node_modules/caniuse-api": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-4.0.0.tgz",
+      "integrity": "sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.0.0",
+        "caniuse-lite": "^1.0.0"
       }
     },
     "node_modules/postcss-reduce-transforms": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz",
-      "integrity": "sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-8.0.1.tgz",
+      "integrity": "sha512-x71slHVykiFi5RuKEXM0wgYpY2PngC78x6R8TnZhHF3lhqt+u/w3MGwYLX+2t5O87ssRiMfEAhQH+3J4QwVzCw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "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==",
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz",
+      "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==",
       "dev": true,
       "license": "MIT"
     },
     "node_modules/postcss-safe-parser": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
-      "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
+      "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
       "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
       "license": "MIT",
       "engines": {
-        "node": ">=12.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
+        "node": ">=18.0"
       },
       "peerDependencies": {
-        "postcss": "^8.3.3"
+        "postcss": "^8.4.31"
       }
     },
     "node_modules/postcss-scss": {
@@ -23551,20 +27401,20 @@
       }
     },
     "node_modules/postcss-svgo": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz",
-      "integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-8.0.1.tgz",
+      "integrity": "sha512-HpnvWii7W0/FPrsejJa6ZTi0kNtTJP/Iba7CUMPX0xPV6QpnndOp+SDP74tFtgjA2cYKYNWJPOlmLXMsvi/9yA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "postcss-value-parser": "^4.2.0",
-        "svgo": "^4.0.0"
+        "svgo": "^4.0.1"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >= 18"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-svgo/node_modules/commander": {
@@ -23578,30 +27428,30 @@
       }
     },
     "node_modules/postcss-svgo/node_modules/css-tree": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
-      "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+      "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "mdn-data": "2.12.2",
-        "source-map-js": "^1.0.1"
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
       },
       "engines": {
         "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
       }
     },
     "node_modules/postcss-svgo/node_modules/mdn-data": {
-      "version": "2.12.2",
-      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
-      "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+      "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
       "dev": true,
       "license": "CC0-1.0"
     },
     "node_modules/postcss-svgo/node_modules/svgo": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
-      "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
+      "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -23611,7 +27461,7 @@
         "css-what": "^6.1.0",
         "csso": "^5.0.5",
         "picocolors": "^1.1.1",
-        "sax": "^1.4.1"
+        "sax": "^1.5.0"
       },
       "bin": {
         "svgo": "bin/svgo.js"
@@ -23625,25 +27475,25 @@
       }
     },
     "node_modules/postcss-unique-selectors": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz",
-      "integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-8.0.1.tgz",
+      "integrity": "sha512-+xvKI5+/Cl8yYQwxDV39Uhuc4WV951xngFvPPjiPj2NIbIfm6vbbRTXblyw0FioLkIoGlw+7qUcY1h2YhaZYgw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "postcss-selector-parser": "^7.1.0"
+        "postcss-selector-parser": "^7.1.2"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/postcss-unique-selectors/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -23661,6 +27511,49 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/postgres-array": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+      "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postgres-bytea": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+      "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-date": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+      "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-interval": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+      "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/prelude-ls": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -23689,9 +27582,9 @@
       }
     },
     "node_modules/prettier-linter-helpers": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
-      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz",
+      "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -23716,17 +27609,43 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "node_modules/pretty-format/node_modules/ansi-styles": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
-      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/pretty-ms": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",
+      "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parse-ms": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+        "node": ">= 0.6.0"
       }
     },
     "node_modules/process-nextick-args": {
@@ -23811,61 +27730,41 @@
       }
     },
     "node_modules/proxy-agent": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz",
-      "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==",
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+      "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "agent-base": "^7.0.2",
+        "agent-base": "^7.1.2",
         "debug": "^4.3.4",
-        "http-proxy-agent": "^7.0.0",
-        "https-proxy-agent": "^7.0.0",
+        "http-proxy-agent": "^7.0.1",
+        "https-proxy-agent": "^7.0.6",
         "lru-cache": "^7.14.1",
-        "pac-proxy-agent": "^7.0.0",
+        "pac-proxy-agent": "^7.1.0",
         "proxy-from-env": "^1.1.0",
-        "socks-proxy-agent": "^8.0.1"
+        "socks-proxy-agent": "^8.0.5"
       },
       "engines": {
         "node": ">= 14"
       }
     },
-    "node_modules/proxy-agent/node_modules/agent-base": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
-      "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+    "node_modules/proxy-agent-negotiate": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz",
+      "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/proxy-agent/node_modules/http-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "agent-base": "^7.1.0",
-        "debug": "^4.3.4"
+        "node": ">= 20"
       },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/proxy-agent/node_modules/https-proxy-agent": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
-      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "agent-base": "^7.1.2",
-        "debug": "4"
+      "peerDependencies": {
+        "kerberos": "^2.0.0"
       },
-      "engines": {
-        "node": ">= 14"
+      "peerDependenciesMeta": {
+        "kerberos": {
+          "optional": true
+        }
       }
     },
     "node_modules/proxy-agent/node_modules/lru-cache": {
@@ -23885,33 +27784,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/ps-list": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmjs.org/ps-list/-/ps-list-8.1.1.tgz",
-      "integrity": "sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/psl": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
-      "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/pump": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@@ -23934,93 +27806,30 @@
       }
     },
     "node_modules/puppeteer-core": {
-      "version": "13.7.0",
-      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz",
-      "integrity": "sha512-rXja4vcnAzFAP1OVLq/5dWNfwBGuzcOARJ6qGV7oAZhnLmVRU8G5MsdeQEAOy332ZhkIOnn9jp15R89LKHyp2Q==",
+      "version": "23.11.1",
+      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz",
+      "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "cross-fetch": "3.1.5",
-        "debug": "4.3.4",
-        "devtools-protocol": "0.0.981744",
-        "extract-zip": "2.0.1",
-        "https-proxy-agent": "5.0.1",
-        "pkg-dir": "4.2.0",
-        "progress": "2.0.3",
-        "proxy-from-env": "1.1.0",
-        "rimraf": "3.0.2",
-        "tar-fs": "2.1.1",
-        "unbzip2-stream": "1.4.3",
-        "ws": "8.5.0"
-      },
-      "engines": {
-        "node": ">=10.18.1"
-      }
-    },
-    "node_modules/puppeteer-core/node_modules/debug": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.1.2"
+        "@puppeteer/browsers": "2.6.1",
+        "chromium-bidi": "0.11.0",
+        "debug": "^4.4.0",
+        "devtools-protocol": "0.0.1367902",
+        "typed-query-selector": "^2.12.0",
+        "ws": "^8.18.0"
       },
       "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
+        "node": ">=18"
       }
     },
     "node_modules/puppeteer-core/node_modules/devtools-protocol": {
-      "version": "0.0.981744",
-      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz",
-      "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==",
+      "version": "0.0.1367902",
+      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
+      "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
       "dev": true,
       "license": "BSD-3-Clause"
     },
-    "node_modules/puppeteer-core/node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/puppeteer-core/node_modules/ws": {
-      "version": "8.5.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
-      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": "^5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/pure-rand": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@@ -24050,14 +27859,35 @@
         "teleport": ">=0.2.0"
       }
     },
+    "node_modules/qified": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz",
+      "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hookified": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/qified/node_modules/hookified": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz",
+      "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/qs": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
-      "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+      "version": "6.15.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+      "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "side-channel": "^1.0.6"
+        "es-define-property": "^1.0.1",
+        "side-channel": "^1.1.1"
       },
       "engines": {
         "node": ">=0.6"
@@ -24066,13 +27896,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/querystringify": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
-      "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/queue-microtask": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -24125,16 +27948,16 @@
       }
     },
     "node_modules/raw-body": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
-      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "bytes": "3.1.2",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "unpipe": "1.0.0"
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "unpipe": "~1.0.0"
       },
       "engines": {
         "node": ">= 0.8"
@@ -24150,6 +27973,27 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/raw-body/node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
     "node_modules/raw-body/node_modules/iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -24163,6 +28007,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/raw-body/node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/rc": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -24221,14 +28075,189 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/react-refresh": {
-      "version": "0.14.2",
-      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
-      "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+    "node_modules/react-is-18": {
+      "name": "react-is",
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/react-is-19": {
+      "name": "react-is",
+      "version": "19.2.7",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
+      "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/react-refresh": {
+      "version": "0.14.2",
+      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+      "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/read-cache": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pify": "^2.3.0"
+      }
+    },
+    "node_modules/read-package-up": {
+      "version": "12.0.0",
+      "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz",
+      "integrity": "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up-simple": "^1.0.1",
+        "read-pkg": "^10.0.0",
+        "type-fest": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-package-up/node_modules/hosted-git-info": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz",
+      "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^11.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/read-package-up/node_modules/lru-cache": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+      "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/read-package-up/node_modules/normalize-package-data": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz",
+      "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^9.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/read-package-up/node_modules/parse-json": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
+      "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "index-to-position": "^1.1.0",
+        "type-fest": "^4.39.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-package-up/node_modules/parse-json/node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-package-up/node_modules/read-pkg": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz",
+      "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.4",
+        "normalize-package-data": "^8.0.0",
+        "parse-json": "^8.3.0",
+        "type-fest": "^5.4.4",
+        "unicorn-magic": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-package-up/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/read-package-up/node_modules/type-fest": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz",
+      "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "dependencies": {
+        "tagged-tag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/read-package-up/node_modules/unicorn-magic": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+      "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/read-pkg": {
@@ -24453,16 +28482,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/redeyed": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz",
-      "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "esprima": "~4.0.0"
-      }
-    },
     "node_modules/reflect.getprototypeof": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -24506,12 +28525,6 @@
         "node": ">=4"
       }
     },
-    "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==",
-      "license": "MIT"
-    },
     "node_modules/regexp.prototype.flags": {
       "version": "1.5.4",
       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -24552,13 +28565,13 @@
       }
     },
     "node_modules/registry-auth-token": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz",
-      "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz",
+      "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@pnpm/npm-conf": "^2.1.0"
+        "@pnpm/npm-conf": "^3.0.2"
       },
       "engines": {
         "node": ">=14"
@@ -24640,13 +28653,6 @@
         "url": "https://opencollective.com/unified"
       }
     },
-    "node_modules/remove-accents": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
-      "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/repeat-string": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
@@ -24658,57 +28664,255 @@
       }
     },
     "node_modules/replace-in-file": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-7.2.0.tgz",
-      "integrity": "sha512-CiLXVop3o8/h2Kd1PwKPPimmS9wUV0Ki6Fl8+1ITD35nB3Gl/PrW5IONpTE0AXk0z4v8WYcpEpdeZqMXvSnWpg==",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-8.4.0.tgz",
+      "integrity": "sha512-D28k8jy2LtUGbCzCnR3znajaTWIjJ/Uee3UdodzcHRxE7zn6NmYW/dcSqyivnsYU3W+MxdX6SbF28NvJ0GRoLA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "chalk": "^4.1.2",
-        "glob": "^8.1.0",
-        "yargs": "^17.7.2"
+        "chalk": "^5.6.2",
+        "glob": "^13.0.0",
+        "yargs": "^18.0.0"
       },
       "bin": {
         "replace-in-file": "bin/cli.js"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       }
     },
-    "node_modules/replace-in-file/node_modules/glob": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-      "deprecated": "Glob versions prior to v9 are no longer supported",
+    "node_modules/replace-in-file/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/brace-expansion": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+      "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/chalk": {
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/cliui": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+      "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^5.0.1",
-        "once": "^1.3.0"
+        "string-width": "^7.2.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=20"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/emoji-regex": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+      "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/replace-in-file/node_modules/glob": {
+      "version": "13.0.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+      "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "minimatch": "^10.2.2",
+        "minipass": "^7.1.3",
+        "path-scurry": "^2.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/replace-in-file/node_modules/lru-cache": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+      "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
     "node_modules/replace-in-file/node_modules/minimatch": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
-      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
       "dev": true,
-      "license": "ISC",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^2.0.1"
+        "brace-expansion": "^5.0.5"
       },
       "engines": {
-        "node": ">=10"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/path-scurry": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+      "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^11.0.0",
+        "minipass": "^7.1.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/replace-in-file/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/yargs": {
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+      "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^9.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "string-width": "^7.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^22.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=23"
+      }
+    },
+    "node_modules/replace-in-file/node_modules/yargs-parser": {
+      "version": "22.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+      "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=23"
+      }
+    },
+    "node_modules/requestidlecallback": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz",
+      "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -24729,6 +28933,21 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/require-in-the-middle": {
+      "version": "7.5.2",
+      "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz",
+      "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.5",
+        "module-details-from-path": "^1.0.3",
+        "resolve": "^1.22.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
     "node_modules/requireindex": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
@@ -24747,19 +28966,23 @@
       "license": "MIT"
     },
     "node_modules/resolve": {
-      "version": "1.22.8",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
-      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+      "version": "1.22.12",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+      "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "is-core-module": "^2.13.0",
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.1",
         "path-parse": "^1.0.7",
         "supports-preserve-symlinks-flag": "^1.0.0"
       },
       "bin": {
         "resolve": "bin/resolve"
       },
+      "engines": {
+        "node": ">= 0.4"
+      },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -24831,10 +29054,20 @@
         "node": ">=8"
       }
     },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
     "node_modules/resolve.exports": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
-      "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+      "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -24889,20 +29122,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/rimraf": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      }
-    },
     "node_modules/robots-parser": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz",
@@ -24913,80 +29132,30 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/rrweb-cssom": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+      "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/rtlcss": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz",
-      "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz",
+      "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "find-up": "^5.0.0",
+        "escalade": "^3.1.1",
         "picocolors": "^1.0.0",
-        "postcss": "^8.3.11",
+        "postcss": "^8.4.21",
         "strip-json-comments": "^3.1.1"
       },
       "bin": {
-        "rtlcss": "bin/rtlcss.js"
-      }
-    },
-    "node_modules/rtlcss-webpack-plugin": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/rtlcss-webpack-plugin/-/rtlcss-webpack-plugin-4.0.7.tgz",
-      "integrity": "sha512-ouSbJtgcLBBQIsMgarxsDnfgRqm/AS4BKls/mz/Xb6HSl+PdEzefTR+Wz5uWQx4odoX0g261Z7yb3QBz0MTm0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "babel-runtime": "~6.25.0",
-        "rtlcss": "^3.5.0"
-      }
-    },
-    "node_modules/rtlcss/node_modules/find-up": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
-      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "locate-path": "^6.0.0",
-        "path-exists": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/rtlcss/node_modules/locate-path": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-locate": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/rtlcss/node_modules/p-locate": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-limit": "^3.0.2"
+        "rtlcss": "bin/rtlcss.js"
       },
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=12.0.0"
       }
     },
     "node_modules/run-con": {
@@ -25040,9 +29209,9 @@
       }
     },
     "node_modules/rxjs": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
-      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
@@ -25133,49 +29302,51 @@
       "license": "MIT"
     },
     "node_modules/sass": {
-      "version": "1.77.6",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz",
-      "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==",
+      "version": "1.101.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz",
+      "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "chokidar": ">=3.0.0 <4.0.0",
-        "immutable": "^4.0.0",
+        "chokidar": "^5.0.0",
+        "immutable": "^5.1.5",
         "source-map-js": ">=0.6.2 <2.0.0"
       },
       "bin": {
         "sass": "sass.js"
       },
       "engines": {
-        "node": ">=14.0.0"
+        "node": ">=20.19.0"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher": "^2.4.1"
       }
     },
     "node_modules/sass-loader": {
-      "version": "12.6.0",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
-      "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
+      "version": "16.0.8",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.8.tgz",
+      "integrity": "sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "klona": "^2.0.4",
         "neo-async": "^2.6.2"
       },
       "engines": {
-        "node": ">= 12.13.0"
+        "node": ">= 18.12.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "fibers": ">= 3.1.0",
-        "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+        "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0",
+        "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
         "sass": "^1.3.0",
         "sass-embedded": "*",
         "webpack": "^5.0.0"
       },
       "peerDependenciesMeta": {
-        "fibers": {
+        "@rspack/core": {
           "optional": true
         },
         "node-sass": {
@@ -25186,15 +29357,51 @@
         },
         "sass-embedded": {
           "optional": true
+        },
+        "webpack": {
+          "optional": true
         }
       }
     },
+    "node_modules/sass/node_modules/chokidar": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+      "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/sass/node_modules/readdirp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+      "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
     "node_modules/sax": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
-      "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+      "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
       "dev": true,
-      "license": "ISC"
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=11.0.0"
+      }
     },
     "node_modules/saxes": {
       "version": "6.0.0",
@@ -25297,46 +29504,46 @@
       }
     },
     "node_modules/semantic-release": {
-      "version": "19.0.5",
-      "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-19.0.5.tgz",
-      "integrity": "sha512-NMPKdfpXTnPn49FDogMBi36SiBfXkSOJqCkk0E4iWOY1tusvvgBwqUmxTX1kmlT6kIYed9YwNKD1sfPpqa5yaA==",
+      "version": "25.0.5",
+      "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.5.tgz",
+      "integrity": "sha512-mn61SUJwtM8ThrWn2WmgLVpwVJeG/hPSupua1psdMoufmwRIPyvRLkRkL0JDXkP67OntlLWUYnBnfVc8EDO3/g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@semantic-release/commit-analyzer": "^9.0.2",
-        "@semantic-release/error": "^3.0.0",
-        "@semantic-release/github": "^8.0.0",
-        "@semantic-release/npm": "^9.0.0",
-        "@semantic-release/release-notes-generator": "^10.0.0",
-        "aggregate-error": "^3.0.0",
-        "cosmiconfig": "^7.0.0",
+        "@semantic-release/commit-analyzer": "^13.0.1",
+        "@semantic-release/error": "^4.0.0",
+        "@semantic-release/github": "^12.0.0",
+        "@semantic-release/npm": "^13.1.1",
+        "@semantic-release/release-notes-generator": "^14.1.0",
+        "aggregate-error": "^5.0.0",
+        "cosmiconfig": "^9.0.0",
         "debug": "^4.0.0",
-        "env-ci": "^5.0.0",
-        "execa": "^5.0.0",
-        "figures": "^3.0.0",
-        "find-versions": "^4.0.0",
+        "env-ci": "^11.0.0",
+        "execa": "^9.0.0",
+        "figures": "^6.0.0",
+        "find-versions": "^6.0.0",
         "get-stream": "^6.0.0",
         "git-log-parser": "^1.2.0",
-        "hook-std": "^2.0.0",
-        "hosted-git-info": "^4.0.0",
-        "lodash": "^4.17.21",
-        "marked": "^4.0.10",
-        "marked-terminal": "^5.0.0",
+        "hook-std": "^4.0.0",
+        "hosted-git-info": "^9.0.0",
+        "import-from-esm": "^2.0.0",
+        "lodash-es": "^4.17.21",
+        "marked": "^15.0.0",
+        "marked-terminal": "^7.3.0",
         "micromatch": "^4.0.2",
-        "p-each-series": "^2.1.0",
-        "p-reduce": "^2.0.0",
-        "read-pkg-up": "^7.0.0",
+        "p-each-series": "^3.0.0",
+        "p-reduce": "^3.0.0",
+        "read-package-up": "^12.0.0",
         "resolve-from": "^5.0.0",
         "semver": "^7.3.2",
-        "semver-diff": "^3.1.1",
         "signale": "^1.2.1",
-        "yargs": "^16.2.0"
+        "yargs": "^18.0.0"
       },
       "bin": {
         "semantic-release": "bin/semantic-release.js"
       },
       "engines": {
-        "node": ">=16 || ^14.17"
+        "node": "^22.14.0 || >= 24.10.0"
       }
     },
     "node_modules/semantic-release-slack-bot": {
@@ -25379,33 +29586,324 @@
         "node": ">=8"
       }
     },
+    "node_modules/semantic-release/node_modules/@semantic-release/error": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+      "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/semantic-release/node_modules/aggregate-error": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+      "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clean-stack": "^5.2.0",
+        "indent-string": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/semantic-release/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/semantic-release/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/semantic-release/node_modules/clean-stack": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz",
+      "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "escape-string-regexp": "5.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/semantic-release/node_modules/cliui": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+      "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.0",
-        "wrap-ansi": "^7.0.0"
+        "string-width": "^7.2.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/semantic-release/node_modules/cosmiconfig": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
+      "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "env-paths": "^2.2.1",
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/semantic-release/node_modules/emoji-regex": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+      "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/semantic-release/node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/execa": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz",
+      "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/merge-streams": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "figures": "^6.1.0",
+        "get-stream": "^9.0.0",
+        "human-signals": "^8.0.1",
+        "is-plain-obj": "^4.1.0",
+        "is-stream": "^4.0.1",
+        "npm-run-path": "^6.0.0",
+        "pretty-ms": "^9.2.0",
+        "signal-exit": "^4.1.0",
+        "strip-final-newline": "^4.0.0",
+        "yoctocolors": "^2.1.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+      "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sec-ant/readable-stream": "^0.4.1",
+        "is-stream": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/hosted-git-info": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz",
+      "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^11.1.0"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
+    "node_modules/semantic-release/node_modules/human-signals": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
+      "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/semantic-release/node_modules/indent-string": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+      "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/is-plain-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/is-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+      "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/js-yaml": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+      "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/semantic-release/node_modules/lru-cache": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+      "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/semantic-release/node_modules/npm-run-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
+      "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0",
+        "unicorn-magic": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/p-reduce": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
+      "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/semantic-release/node_modules/cosmiconfig": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
-      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+    "node_modules/semantic-release/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.2.1",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.10.0"
-      },
       "engines": {
-        "node": ">=10"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/semantic-release/node_modules/semver": {
@@ -25421,43 +29919,110 @@
         "node": ">=10"
       }
     },
-    "node_modules/semantic-release/node_modules/yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+    "node_modules/semantic-release/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
       "dev": true,
       "license": "ISC",
       "engines": {
-        "node": ">= 6"
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/semantic-release/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/semantic-release/node_modules/strip-final-newline": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+      "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/semantic-release/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
       }
     },
     "node_modules/semantic-release/node_modules/yargs": {
-      "version": "16.2.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+      "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "cliui": "^7.0.2",
+        "cliui": "^9.0.1",
         "escalade": "^3.1.1",
         "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.0",
+        "string-width": "^7.2.0",
         "y18n": "^5.0.5",
-        "yargs-parser": "^20.2.2"
+        "yargs-parser": "^22.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": "^20.19.0 || ^22.12.0 || >=23"
       }
     },
     "node_modules/semantic-release/node_modules/yargs-parser": {
-      "version": "20.2.9",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "version": "22.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+      "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
       "dev": true,
       "license": "ISC",
       "engines": {
-        "node": ">=10"
+        "node": "^20.19.0 || ^22.12.0 || >=23"
       }
     },
     "node_modules/semver": {
@@ -25470,27 +30035,14 @@
         "semver": "bin/semver.js"
       }
     },
-    "node_modules/semver-diff": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
-      "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^6.3.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/semver-regex": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
-      "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz",
+      "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">=12"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -25561,13 +30113,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/send/node_modules/ms": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/sentence-case": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz",
@@ -25747,6 +30292,27 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/sha.js": {
+      "version": "2.4.12",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+      "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+      "dev": true,
+      "license": "(MIT AND BSD-3-Clause)",
+      "dependencies": {
+        "inherits": "^2.0.4",
+        "safe-buffer": "^5.2.1",
+        "to-buffer": "^1.2.0"
+      },
+      "bin": {
+        "sha.js": "bin.js"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/shallow-clone": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -25819,16 +30385,23 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/shimmer": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
+      "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
     "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
         "side-channel-map": "^1.0.1",
         "side-channel-weakmap": "^1.0.2"
       },
@@ -25840,14 +30413,14 @@
       }
     },
     "node_modules/side-channel-list": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
-      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3"
+        "object-inspect": "^1.13.4"
       },
       "engines": {
         "node": ">= 0.4"
@@ -26008,6 +30581,53 @@
         "node": ">=4"
       }
     },
+    "node_modules/simple-concat": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/simple-get": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decompress-response": "^6.0.0",
+        "once": "^1.3.1",
+        "simple-concat": "^1.0.0"
+      }
+    },
     "node_modules/simple-git": {
       "version": "3.27.0",
       "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz",
@@ -26057,6 +30677,19 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/skin-tone": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
+      "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "unicode-emoji-modifier-base": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/slackify-markdown": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/slackify-markdown/-/slackify-markdown-4.4.0.tgz",
@@ -26135,13 +30768,13 @@
       }
     },
     "node_modules/socks": {
-      "version": "2.8.4",
-      "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
-      "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
+      "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ip-address": "^9.0.5",
+        "ip-address": "^10.1.1",
         "smart-buffer": "^4.2.0"
       },
       "engines": {
@@ -26164,16 +30797,6 @@
         "node": ">= 14"
       }
     },
-    "node_modules/socks-proxy-agent/node_modules/agent-base": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
-      "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 14"
-      }
-    },
     "node_modules/source-map": {
       "version": "0.7.4",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -26245,10 +30868,11 @@
       "license": "MIT"
     },
     "node_modules/spawnd": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-9.0.2.tgz",
-      "integrity": "sha512-nl8DVHEDQ57IcKakzpjanspVChkMpGLuVwMR/eOn9cXE55Qr6luD2Kn06sA0ootRMdgrU4tInN6lA6ohTNvysw==",
+      "version": "10.1.4",
+      "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-10.1.4.tgz",
+      "integrity": "sha512-drqHc0mKJmtMsiGMOCwzlc5eZ0RPtRvT7tQAluW2A0qUc0G7TQ8KLcn3E6K5qzkLkH2UkS3nYQiVGULvvsD9dw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "signal-exit": "^4.1.0",
         "tree-kill": "^1.2.2"
@@ -26379,44 +31003,6 @@
         "node": ">=8.0"
       }
     },
-    "node_modules/split": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
-      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "through": "2"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/split2": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
-      "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "readable-stream": "^3.0.0"
-      }
-    },
-    "node_modules/split2/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/sprintf-js": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@@ -26424,6 +31010,16 @@
       "dev": true,
       "license": "BSD-3-Clause"
     },
+    "node_modules/stable-hash-x": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
+      "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/stack-utils": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -26464,6 +31060,20 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/stop-iteration-iterator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+      "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "internal-slot": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/stream-combiner2": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
@@ -26476,17 +31086,15 @@
       }
     },
     "node_modules/streamx": {
-      "version": "2.22.0",
-      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz",
-      "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==",
+      "version": "2.28.0",
+      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
+      "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
+        "events-universal": "^1.0.0",
         "fast-fifo": "^1.3.2",
         "text-decoder": "^1.1.0"
-      },
-      "optionalDependencies": {
-        "bare-events": "^2.2.0"
       }
     },
     "node_modules/string_decoder": {
@@ -26793,6 +31401,39 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/strnum": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+      "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "anynum": "^1.0.1"
+      }
+    },
+    "node_modules/stubborn-fs": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz",
+      "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "stubborn-utils": "^1.0.1"
+      }
+    },
+    "node_modules/stubborn-utils": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz",
+      "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/style-search": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -26801,26 +31442,26 @@
       "license": "ISC"
     },
     "node_modules/stylehacks": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.6.tgz",
-      "integrity": "sha512-iitguKivmsueOmTO0wmxURXBP8uqOO+zikLGZ7Mm9e/94R4w5T999Js2taS/KBOnQ/wdC3jN3vNSrkGDrlnqQg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-8.0.1.tgz",
+      "integrity": "sha512-Gv095oTD0N+BdJALNFDsxZpETHZLTxbOl5RyIO7y6VAE6sR3z0MnV3Nix7N0IATNldNTrkvSASp2KR1Yt526HA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "browserslist": "^4.25.1",
-        "postcss-selector-parser": "^7.1.0"
+        "browserslist": "^4.28.2",
+        "postcss-selector-parser": "^7.1.2"
       },
       "engines": {
-        "node": "^18.12.0 || ^20.9.0 || >=22.0"
+        "node": "^22.11.0 || ^24.11.0 || >=26.0"
       },
       "peerDependencies": {
-        "postcss": "^8.4.32"
+        "postcss": "^8.5.15"
       }
     },
     "node_modules/stylehacks/node_modules/postcss-selector-parser": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
-      "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -26832,103 +31473,270 @@
       }
     },
     "node_modules/stylelint": {
-      "version": "14.16.1",
-      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz",
-      "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==",
+      "version": "16.26.1",
+      "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.26.1.tgz",
+      "integrity": "sha512-v20V59/crfc8sVTAtge0mdafI3AdnzQ2KsWe6v523L4OA1bJO02S7MO2oyXDCS6iWb9ckIPnqAFVItqSBQr7jw==",
       "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/stylelint"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/stylelint"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
-        "@csstools/selector-specificity": "^2.0.2",
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-syntax-patches-for-csstree": "^1.0.19",
+        "@csstools/css-tokenizer": "^3.0.4",
+        "@csstools/media-query-list-parser": "^4.0.3",
+        "@csstools/selector-specificity": "^5.0.0",
+        "@dual-bundle/import-meta-resolve": "^4.2.1",
         "balanced-match": "^2.0.0",
         "colord": "^2.9.3",
-        "cosmiconfig": "^7.1.0",
-        "css-functions-list": "^3.1.0",
-        "debug": "^4.3.4",
-        "fast-glob": "^3.2.12",
+        "cosmiconfig": "^9.0.0",
+        "css-functions-list": "^3.2.3",
+        "css-tree": "^3.1.0",
+        "debug": "^4.4.3",
+        "fast-glob": "^3.3.3",
         "fastest-levenshtein": "^1.0.16",
-        "file-entry-cache": "^6.0.1",
+        "file-entry-cache": "^11.1.1",
         "global-modules": "^2.0.0",
         "globby": "^11.1.0",
         "globjoin": "^0.1.4",
-        "html-tags": "^3.2.0",
-        "ignore": "^5.2.1",
-        "import-lazy": "^4.0.0",
+        "html-tags": "^3.3.1",
+        "ignore": "^7.0.5",
         "imurmurhash": "^0.1.4",
         "is-plain-object": "^5.0.0",
-        "known-css-properties": "^0.26.0",
+        "known-css-properties": "^0.37.0",
         "mathml-tag-names": "^2.1.3",
-        "meow": "^9.0.0",
-        "micromatch": "^4.0.5",
+        "meow": "^13.2.0",
+        "micromatch": "^4.0.8",
         "normalize-path": "^3.0.0",
-        "picocolors": "^1.0.0",
-        "postcss": "^8.4.19",
-        "postcss-media-query-parser": "^0.2.3",
-        "postcss-resolve-nested-selector": "^0.1.1",
-        "postcss-safe-parser": "^6.0.0",
-        "postcss-selector-parser": "^6.0.11",
+        "picocolors": "^1.1.1",
+        "postcss": "^8.5.6",
+        "postcss-resolve-nested-selector": "^0.1.6",
+        "postcss-safe-parser": "^7.0.1",
+        "postcss-selector-parser": "^7.1.0",
         "postcss-value-parser": "^4.2.0",
         "resolve-from": "^5.0.0",
         "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1",
-        "style-search": "^0.1.0",
-        "supports-hyperlinks": "^2.3.0",
+        "supports-hyperlinks": "^3.2.0",
         "svg-tags": "^1.0.0",
-        "table": "^6.8.1",
-        "v8-compile-cache": "^2.3.0",
-        "write-file-atomic": "^4.0.2"
+        "table": "^6.9.0",
+        "write-file-atomic": "^5.0.1"
       },
       "bin": {
-        "stylelint": "bin/stylelint.js"
+        "stylelint": "bin/stylelint.mjs"
       },
       "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/stylelint"
+        "node": ">=18.12.0"
       }
     },
     "node_modules/stylelint-config-recommended": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz",
-      "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==",
+      "version": "14.0.1",
+      "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz",
+      "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==",
       "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/stylelint"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/stylelint"
+        }
+      ],
       "license": "MIT",
+      "engines": {
+        "node": ">=18.12.0"
+      },
       "peerDependencies": {
-        "stylelint": "^14.0.0"
+        "stylelint": "^16.1.0"
       }
     },
     "node_modules/stylelint-config-recommended-scss": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz",
-      "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==",
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz",
+      "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "postcss-scss": "^4.0.2",
-        "stylelint-config-recommended": "^6.0.0",
-        "stylelint-scss": "^4.0.0"
+        "postcss-scss": "^4.0.9",
+        "stylelint-config-recommended": "^14.0.1",
+        "stylelint-scss": "^6.4.0"
+      },
+      "engines": {
+        "node": ">=18.12.0"
       },
       "peerDependencies": {
-        "stylelint": "^14.0.0"
+        "postcss": "^8.3.3",
+        "stylelint": "^16.6.1"
+      },
+      "peerDependenciesMeta": {
+        "postcss": {
+          "optional": true
+        }
       }
     },
     "node_modules/stylelint-scss": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.7.0.tgz",
-      "integrity": "sha512-TSUgIeS0H3jqDZnby1UO1Qv3poi1N8wUYIJY6D1tuUq2MN3lwp/rITVo0wD+1SWTmRm0tNmGO0b7nKInnqF6Hg==",
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.14.0.tgz",
+      "integrity": "sha512-ZKmHMZolxeuYsnB+PCYrTpFce0/QWX9i9gh0hPXzp73WjuIMqUpzdQaBCrKoLWh6XtCFSaNDErkMPqdjy1/8aA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
+        "css-tree": "^3.0.1",
+        "is-plain-object": "^5.0.0",
+        "known-css-properties": "^0.37.0",
+        "mdn-data": "^2.25.0",
         "postcss-media-query-parser": "^0.2.3",
-        "postcss-resolve-nested-selector": "^0.1.1",
-        "postcss-selector-parser": "^6.0.11",
+        "postcss-resolve-nested-selector": "^0.1.6",
+        "postcss-selector-parser": "^7.1.1",
         "postcss-value-parser": "^4.2.0"
       },
+      "engines": {
+        "node": ">=18.12.0"
+      },
+      "peerDependencies": {
+        "stylelint": "^16.8.2"
+      }
+    },
+    "node_modules/stylelint-scss/node_modules/css-tree": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+      "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
+    "node_modules/stylelint-scss/node_modules/css-tree/node_modules/mdn-data": {
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+      "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "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-scss/node_modules/mdn-data": {
+      "version": "2.28.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.28.1.tgz",
+      "integrity": "sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/stylelint-scss/node_modules/postcss-selector-parser": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/stylelint/node_modules/@csstools/css-syntax-patches-for-csstree": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz",
+      "integrity": "sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
+      "peerDependencies": {
+        "css-tree": "^3.2.1"
+      },
+      "peerDependenciesMeta": {
+        "css-tree": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz",
+      "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/stylelint/node_modules/@csstools/selector-specificity": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+      "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=18"
+      },
       "peerDependencies": {
-        "stylelint": "^14.5.1 || ^15.0.0"
+        "postcss-selector-parser": "^7.0.0"
       }
     },
+    "node_modules/stylelint/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
     "node_modules/stylelint/node_modules/balanced-match": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
@@ -26937,20 +31745,66 @@
       "license": "MIT"
     },
     "node_modules/stylelint/node_modules/cosmiconfig": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
-      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
+      "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.2.1",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.10.0"
+        "env-paths": "^2.2.1",
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/stylelint/node_modules/css-tree": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+      "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
+    "node_modules/stylelint/node_modules/file-entry-cache": {
+      "version": "11.1.5",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz",
+      "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^6.1.23"
+      }
+    },
+    "node_modules/stylelint/node_modules/flat-cache": {
+      "version": "6.1.23",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz",
+      "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cacheable": "^2.5.0",
+        "flatted": "^3.4.2",
+        "hookified": "^1.15.0"
       }
     },
     "node_modules/stylelint/node_modules/global-modules": {
@@ -26981,6 +31835,16 @@
         "node": ">=6"
       }
     },
+    "node_modules/stylelint/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
     "node_modules/stylelint/node_modules/is-plain-object": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
@@ -26991,6 +31855,29 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/stylelint/node_modules/js-yaml": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+      "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/stylelint/node_modules/kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -27001,14 +31888,83 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/stylelint/node_modules/yaml": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+    "node_modules/stylelint/node_modules/mdn-data": {
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+      "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/stylelint/node_modules/meow": {
+      "version": "13.2.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+      "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/stylelint/node_modules/postcss-selector-parser": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/stylelint/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/stylelint/node_modules/write-file-atomic": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
+      "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/super-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.1.0.tgz",
+      "integrity": "sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==",
       "dev": true,
-      "license": "ISC",
+      "license": "MIT",
+      "dependencies": {
+        "function-timeout": "^1.0.1",
+        "make-asynchronous": "^1.0.1",
+        "time-span": "^5.1.0"
+      },
       "engines": {
-        "node": ">= 6"
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/supports-color": {
@@ -27025,9 +31981,9 @@
       }
     },
     "node_modules/supports-hyperlinks": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
-      "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
+      "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -27035,7 +31991,10 @@
         "supports-color": "^7.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
       }
     },
     "node_modules/supports-preserve-symlinks-flag": {
@@ -27108,13 +32067,13 @@
       "license": "MIT"
     },
     "node_modules/synckit": {
-      "version": "0.11.6",
-      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
-      "integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
+      "version": "0.11.13",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz",
+      "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@pkgr/core": "^0.2.4"
+        "@pkgr/core": "^0.3.6"
       },
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
@@ -27124,9 +32083,9 @@
       }
     },
     "node_modules/table": {
-      "version": "6.8.2",
-      "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz",
-      "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==",
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz",
+      "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
@@ -27141,16 +32100,16 @@
       }
     },
     "node_modules/table/node_modules/ajv": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz",
-      "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==",
+      "version": "8.20.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+      "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
         "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2",
-        "uri-js": "^4.4.1"
+        "require-from-string": "^2.0.2"
       },
       "funding": {
         "type": "github",
@@ -27164,12 +32123,18 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/tailwindcss": {
-      "version": "4.1.17",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
-      "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
+    "node_modules/tagged-tag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+      "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
     },
     "node_modules/tannin": {
       "version": "1.2.0",
@@ -27182,171 +32147,122 @@
       }
     },
     "node_modules/tapable": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+      "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
       "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
       }
     },
     "node_modules/tar-fs": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
-      "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
+      "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "chownr": "^1.1.1",
-        "mkdirp-classic": "^0.5.2",
         "pump": "^3.0.0",
-        "tar-stream": "^2.1.4"
+        "tar-stream": "^3.1.5"
+      },
+      "optionalDependencies": {
+        "bare-fs": "^4.0.1",
+        "bare-path": "^3.0.0"
       }
     },
     "node_modules/tar-stream": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
-      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz",
+      "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "bl": "^4.0.3",
-        "end-of-stream": "^1.4.1",
-        "fs-constants": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^3.1.1"
-      },
-      "engines": {
-        "node": ">=6"
+        "b4a": "^1.6.4",
+        "bare-fs": "^4.5.5",
+        "fast-fifo": "^1.2.0",
+        "streamx": "^2.15.0"
       }
     },
-    "node_modules/tar-stream/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+    "node_modules/tar-stream/node_modules/b4a": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+      "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
       "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "react-native-b4a": "*"
       },
-      "engines": {
-        "node": ">= 6"
+      "peerDependenciesMeta": {
+        "react-native-b4a": {
+          "optional": true
+        }
       }
     },
-    "node_modules/temp-dir": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
-      "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
+    "node_modules/teex": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+      "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=8"
+      "dependencies": {
+        "streamx": "^2.12.5"
       }
     },
-    "node_modules/tempy": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz",
-      "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==",
+    "node_modules/temp-dir": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz",
+      "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "del": "^6.0.0",
-        "is-stream": "^2.0.0",
-        "temp-dir": "^2.0.0",
-        "type-fest": "^0.16.0",
-        "unique-string": "^2.0.0"
-      },
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=14.16"
       }
     },
-    "node_modules/tempy/node_modules/del": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
-      "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==",
+    "node_modules/tempy": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.2.0.tgz",
+      "integrity": "sha512-d79HhZya5Djd7am0q+W4RTsSU+D/aJzM+4Y4AGJGuGlgM2L6sx5ZvOYTmZjqPhrDrV6xJTtRSm1JCLj6V6LHLQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "globby": "^11.0.1",
-        "graceful-fs": "^4.2.4",
-        "is-glob": "^4.0.1",
-        "is-path-cwd": "^2.2.0",
-        "is-path-inside": "^3.0.2",
-        "p-map": "^4.0.0",
-        "rimraf": "^3.0.2",
-        "slash": "^3.0.0"
+        "is-stream": "^3.0.0",
+        "temp-dir": "^3.0.0",
+        "type-fest": "^2.12.2",
+        "unique-string": "^3.0.0"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=14.16"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/tempy/node_modules/p-map": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
-      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+    "node_modules/tempy/node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "aggregate-error": "^3.0.0"
-      },
       "engines": {
-        "node": ">=10"
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/tempy/node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/tempy/node_modules/type-fest": {
-      "version": "0.16.0",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
-      "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+      "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
       "dev": true,
       "license": "(MIT OR CC0-1.0)",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/terminal-link": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
-      "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-escapes": "^4.2.1",
-        "supports-hyperlinks": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
+        "node": ">=12.20"
       },
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
@@ -27480,61 +32396,58 @@
         "node": ">=8"
       }
     },
-    "node_modules/test-exclude/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/text-decoder": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+      "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
       "dev": true,
-      "license": "MIT",
+      "license": "Apache-2.0",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "b4a": "^1.6.4"
       }
     },
-    "node_modules/test-exclude/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/text-decoder/node_modules/b4a": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+      "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
       "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "react-native-b4a": "*"
       },
-      "engines": {
-        "node": "*"
+      "peerDependenciesMeta": {
+        "react-native-b4a": {
+          "optional": true
+        }
       }
     },
-    "node_modules/text-decoder": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
-      "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+    "node_modules/thenify": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
       "dev": true,
-      "license": "Apache-2.0",
+      "license": "MIT",
       "dependencies": {
-        "b4a": "^1.6.4"
+        "any-promise": "^1.0.0"
       }
     },
-    "node_modules/text-extensions": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",
-      "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==",
+    "node_modules/thenify-all": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
       "dev": true,
       "license": "MIT",
+      "dependencies": {
+        "thenify": ">= 3.1.0 < 4"
+      },
       "engines": {
-        "node": ">=0.10"
+        "node": ">=0.8"
       }
     },
-    "node_modules/text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/third-party-web": {
-      "version": "0.23.4",
-      "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.23.4.tgz",
-      "integrity": "sha512-kwYnSZRhEvv0SBW2fp8SBBKRglMoBjV8xz6C31m0ewqOtknB5UL+Ihg+M81hyFY5ldkZuGWPb+e4GVDkzf/gYg==",
+      "version": "0.27.0",
+      "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.27.0.tgz",
+      "integrity": "sha512-h0JYX+dO2Zr3abCQpS6/uFjujaOjA1DyDzGQ41+oFn9VW/ARiq9g5ln7qEP9+BTzDpOMyIfsfj4OvfgXAsMUSA==",
       "dev": true,
       "license": "MIT"
     },
@@ -27545,35 +32458,111 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/through2": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
-      "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+    "node_modules/thunky": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/time-span": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz",
+      "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "readable-stream": "3"
+        "convert-hrtime": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/through2/node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+    "node_modules/tinyglobby": {
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
       },
       "engines": {
-        "node": ">= 6"
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
       }
     },
-    "node_modules/thunky": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
-      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+    "node_modules/tinyglobby/node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tinyglobby/node_modules/picomatch": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+      "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/tldts": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+      "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tldts-core": "^6.1.86"
+      },
+      "bin": {
+        "tldts": "bin/cli.js"
+      }
+    },
+    "node_modules/tldts-core": {
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.6.tgz",
+      "integrity": "sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tldts-icann": {
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/tldts-icann/-/tldts-icann-7.4.6.tgz",
+      "integrity": "sha512-T7sT7XcBzPJY5OJo7DIFiH9Wbh7iO1rBXHq681Caghnke9EjXTNXsuJ/re03TzGBB7DWWzQ6sDcYtM1rfRMHVg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tldts-core": "^7.4.6"
+      }
+    },
+    "node_modules/tldts/node_modules/tldts-core": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+      "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
       "dev": true,
       "license": "MIT"
     },
@@ -27590,6 +32579,26 @@
         "node": ">=0.6.0"
       }
     },
+    "node_modules/tmp-promise": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
+      "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tmp": "^0.2.0"
+      }
+    },
+    "node_modules/tmp-promise/node_modules/tmp": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
+      "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
     "node_modules/tmpl": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -27597,6 +32606,21 @@
       "dev": true,
       "license": "BSD-3-Clause"
     },
+    "node_modules/to-buffer": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+      "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "isarray": "^2.0.5",
+        "safe-buffer": "^5.2.1",
+        "typed-array-buffer": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -27631,42 +32655,29 @@
       }
     },
     "node_modules/tough-cookie": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
-      "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+      "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "psl": "^1.1.33",
-        "punycode": "^2.1.1",
-        "universalify": "^0.2.0",
-        "url-parse": "^1.5.3"
+        "tldts": "^6.1.32"
       },
       "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/tough-cookie/node_modules/universalify": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
-      "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4.0.0"
+        "node": ">=16"
       }
     },
     "node_modules/tr46": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
-      "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+      "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "punycode": "^2.1.1"
+        "punycode": "^2.3.1"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/traverse": {
@@ -27737,16 +32748,16 @@
       }
     },
     "node_modules/ts-api-utils": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
-      "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+      "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=16"
+        "node": ">=18.12"
       },
       "peerDependencies": {
-        "typescript": ">=4.2.0"
+        "typescript": ">=4.8.4"
       }
     },
     "node_modules/tsconfig-paths": {
@@ -27786,34 +32797,21 @@
       }
     },
     "node_modules/tslib": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
-      "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
       "license": "0BSD"
     },
-    "node_modules/tsutils": {
-      "version": "3.21.0",
-      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
-      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+    "node_modules/tunnel": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "tslib": "^1.8.1"
-      },
       "engines": {
-        "node": ">= 6"
-      },
-      "peerDependencies": {
-        "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
       }
     },
-    "node_modules/tsutils/node_modules/tslib": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-      "dev": true,
-      "license": "0BSD"
-    },
     "node_modules/type-check": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -27942,23 +32940,13 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+    "node_modules/typed-query-selector": {
+      "version": "2.12.2",
+      "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
+      "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/typedarray-to-buffer": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
-      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-typedarray": "^1.0.0"
-      }
-    },
     "node_modules/typescript": {
       "version": "5.5.3",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
@@ -27974,6 +32962,30 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/typescript-eslint": {
+      "version": "8.62.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz",
+      "integrity": "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.62.1",
+        "@typescript-eslint/parser": "8.62.1",
+        "@typescript-eslint/typescript-estree": "8.62.1",
+        "@typescript-eslint/utils": "8.62.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
     "node_modules/uc.micro": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
@@ -27982,9 +32994,9 @@
       "license": "MIT"
     },
     "node_modules/uglify-js": {
-      "version": "3.18.0",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz",
-      "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==",
+      "version": "3.19.3",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
+      "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
       "dev": true,
       "license": "BSD-2-Clause",
       "optional": true,
@@ -28049,10 +33061,20 @@
         "node": "*"
       }
     },
+    "node_modules/undici": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
+      "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.18.1"
+      }
+    },
     "node_modules/undici-types": {
-      "version": "5.26.5",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
       "dev": true,
       "license": "MIT"
     },
@@ -28066,6 +33088,16 @@
         "node": ">=4"
       }
     },
+    "node_modules/unicode-emoji-modifier-base": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
+      "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/unicode-match-property-ecmascript": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
@@ -28100,6 +33132,19 @@
         "node": ">=4"
       }
     },
+    "node_modules/unicorn-magic": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+      "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/unified": {
       "version": "9.2.2",
       "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz",
@@ -28154,16 +33199,19 @@
       }
     },
     "node_modules/unique-string": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
-      "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
+      "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "crypto-random-string": "^2.0.0"
+        "crypto-random-string": "^4.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/unist-util-is": {
@@ -28236,10 +33284,21 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/universal-github-app-jwt": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz",
+      "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/jsonwebtoken": "^9.0.0",
+        "jsonwebtoken": "^9.0.2"
+      }
+    },
     "node_modules/universal-user-agent": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
-      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
+      "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
       "dev": true,
       "license": "ISC"
     },
@@ -28263,10 +33322,48 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/unrs-resolver": {
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz",
+      "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "napi-postinstall": "^0.3.4"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unrs-resolver"
+      },
+      "optionalDependencies": {
+        "@unrs/resolver-binding-android-arm-eabi": "1.12.2",
+        "@unrs/resolver-binding-android-arm64": "1.12.2",
+        "@unrs/resolver-binding-darwin-arm64": "1.12.2",
+        "@unrs/resolver-binding-darwin-x64": "1.12.2",
+        "@unrs/resolver-binding-freebsd-x64": "1.12.2",
+        "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2",
+        "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2",
+        "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-arm64-musl": "1.12.2",
+        "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-loong64-musl": "1.12.2",
+        "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2",
+        "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-x64-gnu": "1.12.2",
+        "@unrs/resolver-binding-linux-x64-musl": "1.12.2",
+        "@unrs/resolver-binding-openharmony-arm64": "1.12.2",
+        "@unrs/resolver-binding-wasm32-wasi": "1.12.2",
+        "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2",
+        "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2",
+        "@unrs/resolver-binding-win32-x64-msvc": "1.12.2"
+      }
+    },
     "node_modules/update-browserslist-db": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
-      "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
       "dev": true,
       "funding": [
         {
@@ -28282,6 +33379,7 @@
           "url": "https://github.com/sponsors/ai"
         }
       ],
+      "license": "MIT",
       "dependencies": {
         "escalade": "^3.2.0",
         "picocolors": "^1.1.1"
@@ -28322,11 +33420,14 @@
       }
     },
     "node_modules/url-join": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
-      "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
+      "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
     },
     "node_modules/url-loader": {
       "version": "4.1.1",
@@ -28375,15 +33476,14 @@
         "url": "https://opencollective.com/webpack"
       }
     },
-    "node_modules/url-parse": {
-      "version": "1.5.10",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
-      "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+    "node_modules/use-memo-one": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+      "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
       "dev": true,
       "license": "MIT",
-      "dependencies": {
-        "querystringify": "^2.1.1",
-        "requires-port": "^1.0.0"
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
       }
     },
     "node_modules/util-deprecate": {
@@ -28413,13 +33513,6 @@
         "uuid": "dist/bin/uuid"
       }
     },
-    "node_modules/v8-compile-cache": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
-      "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/v8-to-istanbul": {
       "version": "9.3.0",
       "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
@@ -28547,30 +33640,30 @@
       }
     },
     "node_modules/w3c-xmlserializer": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
-      "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "xml-name-validator": "^4.0.0"
+        "xml-name-validator": "^5.0.0"
       },
       "engines": {
-        "node": ">=14"
+        "node": ">=18"
       }
     },
     "node_modules/wait-on": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz",
-      "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==",
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.5.tgz",
+      "integrity": "sha512-J3WlS0txVHkhLRb2FsmRg3dkMTCV1+M6Xra3Ho7HzZDHpE7DCOnoSoCJsZotrmW3uRMhvIJGSKUKrh/MeF4iag==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "axios": "^1.6.1",
-        "joi": "^17.11.0",
+        "axios": "^1.12.1",
+        "joi": "^18.0.1",
         "lodash": "^4.17.21",
         "minimist": "^1.2.8",
-        "rxjs": "^7.8.1"
+        "rxjs": "^7.8.2"
       },
       "bin": {
         "wait-on": "bin/wait-on"
@@ -28589,6 +33682,13 @@
         "makeerror": "1.0.12"
       }
     },
+    "node_modules/wasm-feature-detect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
+      "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
     "node_modules/watchpack": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
@@ -28624,9 +33724,23 @@
       }
     },
     "node_modules/web-vitals": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz",
-      "integrity": "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==",
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
+      "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/web-worker": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz",
+      "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/webdriver-bidi-protocol": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
+      "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
       "dev": true,
       "license": "Apache-2.0"
     },
@@ -28803,21 +33917,6 @@
         "node": ">=14"
       }
     },
-    "node_modules/webpack-cli/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
     "node_modules/webpack-cli/node_modules/interpret": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
@@ -28841,45 +33940,6 @@
         "node": ">= 10.13.0"
       }
     },
-    "node_modules/webpack-cli/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/webpack-cli/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/webpack-cli/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
     "node_modules/webpack-dev-middleware": {
       "version": "5.3.4",
       "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
@@ -29070,42 +34130,50 @@
       }
     },
     "node_modules/whatwg-encoding": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
-      "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+      "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "iconv-lite": "0.6.3"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/whatwg-mimetype": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
-      "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/whatwg-url": {
-      "version": "11.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
-      "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+      "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "tr46": "^3.0.0",
+        "tr46": "^5.1.0",
         "webidl-conversions": "^7.0.0"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
+    "node_modules/when-exit": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz",
+      "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -29291,9 +34359,9 @@
       }
     },
     "node_modules/ws": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
-      "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+      "version": "8.21.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+      "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -29313,23 +34381,66 @@
       }
     },
     "node_modules/xdg-basedir": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
-      "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
+      "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/xml-name-validator": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
-      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
       "dev": true,
       "license": "Apache-2.0",
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
+      }
+    },
+    "node_modules/xml-naming": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
+      "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/xml2js": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+      "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
       }
     },
     "node_modules/xmlchars": {
@@ -29432,6 +34543,19 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/yoctocolors": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
+      "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/yoctocolors-cjs": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
@@ -29445,6 +34569,16 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/zod": {
+      "version": "3.23.8",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+      "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    },
     "node_modules/zwitch": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz",
diff --git a/package.json b/package.json
index 9f29748f..c9025f77 100644
--- a/package.json
+++ b/package.json
@@ -49,29 +49,24 @@
     "@semantic-release/changelog": "^6.0.3",
     "@semantic-release/exec": "^6.0.3",
     "@semantic-release/git": "^10.0.1",
-    "@tailwindcss/postcss": "^4.1.17",
-    "@wordpress/env": "^10.32.0",
-    "@wordpress/eslint-plugin": "^22.20.0",
-    "@wordpress/scripts": "^27.9.0",
+    "@wordpress/e2e-test-utils-playwright": "^1.50.0",
+    "@wordpress/env": "^11.10.0",
+    "@wordpress/scripts": "^32.6.0",
     "conventional-changelog-simple-preset": "^1.0.24",
-    "cssnano": "^7.1.1",
-    "eslint-config-wordpress": "^2.0.0",
-    "filenamify": "^4.3.0",
-    "grunt": "^1.6.1",
-    "grunt-version": "^3.0.1",
+    "cssnano": "^8.0.2",
+    "filenamify": "^7.0.2",
+    "grunt": "^1.6.2",
+    "grunt-version": "^3.0.2",
     "grunt-wp-readme-to-markdown": "^2.1.0",
     "npm-run-all": "^4.1.5",
-    "postcss": "^8.5.6",
-    "replace-in-file": "^7.1.0",
-    "semantic-release": "^19.0.5",
+    "postcss": "^8.5.16",
+    "replace-in-file": "^8.4.0",
+    "semantic-release": "^25.0.5",
     "semantic-release-slack-bot": "^4.0.2",
-    "simple-git-hooks": "^2.13.1",
-    "tailwindcss": "^4.1.11"
+    "simple-git-hooks": "^2.13.1"
   },
   "dependencies": {
-    "@wordpress/icons": "^10.32.0",
-    "chart.js": "^4.5.0",
-    "classnames": "^2.5.1",
-    "object-hash": "^3.0.0"
+    "@wordpress/icons": "^15.1.0",
+    "chart.js": "^4.5.1"
   }
 }
diff --git a/patches/qdrant-response-returntypewillchange.patch b/patches/qdrant-response-returntypewillchange.patch
new file mode 100644
index 00000000..8512c7ee
--- /dev/null
+++ b/patches/qdrant-response-returntypewillchange.patch
@@ -0,0 +1,10 @@
+--- a/src/Response.php
++++ b/src/Response.php
+@@ -45,6 +45,7 @@
+         return isset($this->raw[$offset]);
+     }
+
++    #[\ReturnTypeWillChange]
+     public function offsetGet($offset)
+     {
+         return $this->raw[$offset];
diff --git a/phpcs.xml b/phpcs.xml
index 06a21583..0ce69d0c 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -45,6 +45,15 @@
 
     
 
+    
+        
+            
+                
+                
+            
+        
+    
+
     
     
 
diff --git a/postcss.config.js b/postcss.config.js
index fa02c607..3a7ec4cc 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,6 +1,5 @@
 module.exports = {
 	plugins: {
-		'@tailwindcss/postcss': {},
-		...( 'production' === process.env.NODE_ENV ? { cssnano: {}} : {})
-	}
+		...( 'production' === process.env.NODE_ENV ? { cssnano: {}} : {} ),
+	},
 };
diff --git a/src/backend/App.js b/src/backend/App.js
index e79c74dc..88bf86f9 100644
--- a/src/backend/App.js
+++ b/src/backend/App.js
@@ -1,32 +1,70 @@
 /**
  * WordPress dependencies.
  */
-import apiFetch from '@wordpress/api-fetch';
+import { __ } from '@wordpress/i18n';
 
-import { Spinner } from '@wordpress/components';
+import apiFetch from '@wordpress/api-fetch';
 
-import { useSelect, useDispatch } from '@wordpress/data';
+import { useDispatch, useSelect } from '@wordpress/data';
 
 import { useEffect } from '@wordpress/element';
 
-import { applyFilters } from '@wordpress/hooks';
-
 /**
  * Internal dependencies.
  */
-import { ROUTE_TREE as ROUTE } from './route';
-import Sidebar from './parts/Sidebar';
-import Notices from './parts/Notices';
-import { ErrorSection } from './parts/ErrorSection';
+import './style.scss';
+import { getAccessibleRoutes, navigate, useRoute } from './router';
+import HeaderBar from './components/HeaderBar';
+import TabNav from './components/TabNav';
+import SideNav from './components/SideNav';
+import Notices from './components/Notices';
+import ServiceErrors from './components/ServiceErrors';
+import Card from './components/Card';
+import Chip from './components/Chip';
+import ChunkLimitNotice from './components/ChunkLimitNotice';
+import DataTable from './components/DataTable';
+import FieldRow from './components/FieldRow';
+import ModerationModal from './components/ModerationModal';
+import Pagination from './components/Pagination';
+import Dashboard from './screens/Dashboard';
+import KnowledgeBase from './screens/KnowledgeBase';
+import Messages from './screens/Messages';
+import Settings from './screens/Settings';
+
+// The layout kit and router, bridged for the pro bundle: panels registered
+// via `hyve.routes` / `hyve.slot` build on these.
+window.hyveComponents = window.hyveComponents || {};
+window.hyveComponents.ui = {
+	Card,
+	Chip,
+	ChunkLimitNotice,
+	DataTable,
+	FieldRow,
+	ModerationModal,
+	Pagination,
+	navigate,
+};
+
+const SCREENS = {
+	dashboard: Dashboard,
+	kb: KnowledgeBase,
+	messages: Messages,
+	settings: Settings,
+};
 
 const App = () => {
-	const hasLoaded = useSelect( ( select ) => select( 'hyve' ).hasLoaded() );
-	const route = useSelect( ( select ) => select( 'hyve' ).getRoute() );
+	const { screen, sub, item } = useRoute();
+
+	const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() );
 
-	const { setSettings, setLoading, setRoute } = useDispatch( 'hyve' );
+	const attentionCount = useSelect( ( select ) =>
+		select( 'hyve' ).getAttentionCount()
+	);
+
+	const { setSettings, setLoading } = useDispatch( 'hyve' );
 
 	useEffect( () => {
-		const fetchData = async () => {
+		const fetchSettings = async () => {
 			const response = await apiFetch( {
 				path: `${ window.hyve.api }/settings`,
 			} );
@@ -34,71 +72,141 @@ const App = () => {
 			setSettings( response );
 			setLoading();
 
-			const sdkEvent = new Event( 'themeisle:banner:init' );
-			document.dispatchEvent( sdkEvent );
+			// The Themeisle SDK waits for this before injecting campaign
+			// banners into #tsdk_banner.
+			document.dispatchEvent( new Event( 'themeisle:banner:init' ) );
 		};
 
-		fetchData();
+		// Support users without full access cannot read settings; skip the
+		// request so the app finishes loading instead of hanging on a 403.
+		if ( window.hyve?.canManage ) {
+			fetchSettings();
+		} else {
+			setLoading();
+		}
 
-		const urlParams = new URLSearchParams( window.location.search );
-		const nav = urlParams.get( 'nav' );
+		window.tsdk_reposition_notice?.();
+	}, [ setSettings, setLoading ] );
 
-		if ( nav ) {
-			setRoute( nav );
-		}
+	const routes = getAccessibleRoutes();
+	const current = routes[ screen ];
+	const Screen = SCREENS[ screen ];
 
-		if ( window.tsdk_reposition_notice ) {
-			window.tsdk_reposition_notice();
+	useEffect( () => {
+		if ( hasAPI || ! current ) {
+			return;
 		}
-	}, [ setSettings, setLoading, setRoute ] );
 
-	const ROUTE_TREE = applyFilters( 'hyve.route', ROUTE );
-
-	const ROUTE_COMPONENTS = Object.keys( ROUTE_TREE ).reduce( ( acc, key ) => {
-		if ( ROUTE_TREE[ key ].component ) {
-			acc[ key ] = ROUTE_TREE[ key ].component;
+		if ( current.requiresAPI ) {
+			navigate( 'dashboard', null, { replace: true } );
+			return;
 		}
 
-		if ( ROUTE_TREE[ key ].children ) {
-			Object.keys( ROUTE_TREE[ key ].children ).forEach( ( childKey ) => {
-				acc[ childKey ] =
-					ROUTE_TREE[ key ].children[ childKey ].component;
-			} );
+		// Key-gated sub-panel without a key: land on the first open panel.
+		if ( sub && current.subs?.[ sub ]?.requiresAPI ) {
+			const fallback = Object.keys( current.subs ).find(
+				( key ) =>
+					! current.subs[ key ].requiresAPI &&
+					! current.subs[ key ].hidden
+			);
+
+			if ( fallback ) {
+				navigate( screen, fallback, { replace: true } );
+			}
 		}
+	}, [ hasAPI, current, screen, sub ] );
+
+	const subs = current?.subs
+		? Object.entries( current.subs ).filter(
+				( [ , entry ] ) => ! entry.hidden
+		  )
+		: [];
+
+	const screenContent = Screen ? (
+		
+	) : (
+		
+

+ { __( + 'This screen is on its way. Use the tabs to move around; the URL updates so every view is linkable and browser back/forward works.', + 'hyve-lite' + ) } +

+
+ ); - return acc; - }, {} ); + return ( +
+ - const Page = ROUTE_COMPONENTS[ route ] || null; + - return ( - <> - { ! hasLoaded && ( -
- -
- ) } - -
+
+ { /* Campaign banner slot, hidden for licensed pro users. */ }
- -
-
- + - { Page && } + { current && ( +
+

{ current.label }

+ { current.description && ( +

{ current.description }

+ ) } +
+ ) } + + { current?.sidebar ? ( +
+ +
+ { screenContent } +
-
+ ) : ( + <> + { 1 < subs.length && ( +
+ { subs.map( ( [ key, entry ] ) => ( + + ) ) } +
+ ) } + + { screenContent } + + ) }
- +
); }; diff --git a/src/backend/components/Card.js b/src/backend/components/Card.js new file mode 100644 index 00000000..c0d095c8 --- /dev/null +++ b/src/backend/components/Card.js @@ -0,0 +1,22 @@ +const Card = ( { title, actions, footer, children } ) => { + return ( +
+ { ( title || actions ) && ( +
+ { title &&

{ title }

} + { actions && ( +
+ { actions } +
+ ) } +
+ ) } + + { children } + + { footer &&
{ footer }
} +
+ ); +}; + +export default Card; diff --git a/src/backend/components/Chip.js b/src/backend/components/Chip.js new file mode 100644 index 00000000..7a776e3a --- /dev/null +++ b/src/backend/components/Chip.js @@ -0,0 +1,10 @@ +const Chip = ( { tone = 'muted', dot = true, children } ) => { + return ( + + { dot && } + { children } + + ); +}; + +export default Chip; diff --git a/src/backend/components/ChunkLimitNotice.js b/src/backend/components/ChunkLimitNotice.js new file mode 100644 index 00000000..aac0c8f5 --- /dev/null +++ b/src/backend/components/ChunkLimitNotice.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { useSelect } from '@wordpress/data'; + +/** + * Shared chunk-limit warning, shown in every add-content flow once the free + * knowledge base limit is reached (never with Qdrant active). + */ +const ChunkLimitNotice = () => { + const hasReachedLimit = useSelect( ( select ) => + select( 'hyve' ).hasReachedLimit() + ); + + if ( ! hasReachedLimit ) { + return null; + } + + return ( +
+
+

+ { __( + 'You have reached the limit of posts that can be added to the Knowledge Base. Please delete existing posts if you wish to add more.', + 'hyve-lite' + ) } +

+
+
+ ); +}; + +export default ChunkLimitNotice; diff --git a/src/backend/components/DataTable.js b/src/backend/components/DataTable.js new file mode 100644 index 00000000..4402ceff --- /dev/null +++ b/src/backend/components/DataTable.js @@ -0,0 +1,118 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { Button, Spinner } from '@wordpress/components'; + +/** + * Shared data table. Columns: `{ key, label, align: 'num'|'actions'|'check', + * render( row ), renderHeader() }`; `render` falls back to `row[ key ]`, and + * `renderHeader` replaces the label (used for select-all checkboxes). + * Screen-reader-only header for action columns. + * + * @param {Object} props Component props. + * @param {Array} props.columns Column definitions. + * @param {Array} props.rows Row objects. + * @param {Function} props.rowKey Row key getter; defaults to `row.ID`. + * @param {boolean} props.isLoading Whether a fetch is in flight. + * @param {boolean} props.hasMore Whether more rows can be loaded. + * @param {?Function} props.onLoadMore Load-more handler. + * @param {string} props.empty Empty-state message. + */ +const DataTable = ( { + columns, + rows, + rowKey = ( row ) => row.ID, + isLoading = false, + hasMore = false, + onLoadMore, + empty = __( 'No data found.', 'hyve-lite' ), +} ) => { + const cellClass = ( column ) => { + if ( 'num' === column.align ) { + return 'hyve-next-table__num'; + } + + if ( 'actions' === column.align ) { + return 'hyve-next-table__actions'; + } + + if ( 'check' === column.align ) { + return 'hyve-next-table__check'; + } + + return undefined; + }; + + const headerContent = ( column ) => { + if ( column.renderHeader ) { + return column.renderHeader(); + } + + if ( 'actions' === column.align ) { + return { column.label }; + } + + return column.label; + }; + + return ( + <> + { 0 < rows.length && ( +
+ + + + { columns.map( ( column ) => ( + + ) ) } + + + + { rows.map( ( row ) => ( + + { columns.map( ( column ) => ( + + ) ) } + + ) ) } + +
+ { headerContent( column ) } +
+ { column.render + ? column.render( row ) + : row[ column.key ] } +
+
+ ) } + + { ! isLoading && 0 === rows.length && ( +

{ empty }

+ ) } + + { isLoading && ( +
+ +
+ ) } + + { hasMore && ! isLoading && ( +
+ +
+ ) } + + ); +}; + +export default DataTable; diff --git a/src/backend/components/FieldRow.js b/src/backend/components/FieldRow.js new file mode 100644 index 00000000..b5803af6 --- /dev/null +++ b/src/backend/components/FieldRow.js @@ -0,0 +1,19 @@ +const FieldRow = ( { label, description, wide = false, children } ) => { + return ( +
+
+ { label } + { description &&

{ description }

} +
+
+ { children } +
+
+ ); +}; + +export default FieldRow; diff --git a/src/backend/components/HeaderBar.js b/src/backend/components/HeaderBar.js new file mode 100644 index 00000000..caaa5c4e --- /dev/null +++ b/src/backend/components/HeaderBar.js @@ -0,0 +1,126 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import { Icon } from '@wordpress/components'; + +import { useSelect } from '@wordpress/data'; + +import { help } from '@wordpress/icons'; + +/** + * Internal dependencies. + */ +import { navigate } from '../router'; +import { setUtm } from '../utils'; + +// Pro localizes `window.hyve.license`; without it there is nothing to show. +const getLicenseStatus = () => { + const license = window.hyve?.license; + + if ( ! license ) { + return null; + } + + if ( 'valid' === license.valid || 'valid' === license.license ) { + return { + tone: 'ok', + label: __( 'Pro', 'hyve-lite' ), + title: license.expiration + ? sprintf( + /* translators: %s is the expiration date. */ + __( 'Expires %s', 'hyve-lite' ), + license.expiration + ) + : undefined, + }; + } + + if ( 'active_expired' === license.valid ) { + return { + tone: 'bad', + label: __( 'Pro · Expired', 'hyve-lite' ), + }; + } + + return { + tone: 'warn', + label: __( 'Pro · Inactive', 'hyve-lite' ), + }; +}; + +const HeaderBar = () => { + const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); + + const status = hasAPI + ? { tone: 'live', label: __( 'API connected', 'hyve-lite' ) } + : { tone: 'setup', label: __( 'API not connected', 'hyve-lite' ) }; + + const licenseStatus = getLicenseStatus(); + + return ( +
+ ); +}; + +export default HeaderBar; diff --git a/src/backend/components/KnowledgeBaseOptions.js b/src/backend/components/KnowledgeBaseOptions.js deleted file mode 100644 index 5cf9a728..00000000 --- a/src/backend/components/KnowledgeBaseOptions.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { Button, Panel, PanelRow, RangeControl } from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -export const KnowledgeBaseOptions = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const { setSetting } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error?.message, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - return ( - - - { - setSetting( 'similarity_score_threshold', newValue ); - } } - /> - - - - - - ); -}; diff --git a/src/backend/components/ModerationModal.js b/src/backend/components/ModerationModal.js new file mode 100644 index 00000000..dfd9b7d1 --- /dev/null +++ b/src/backend/components/ModerationModal.js @@ -0,0 +1,135 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import { Button, Modal } from '@wordpress/components'; + +import { useState } from '@wordpress/element'; + +import { info } from '@wordpress/icons'; + +/** + * Internal dependencies. + */ +import { moderationLabels, onProcessData } from '../utils'; + +/** + * Moderation review modal: flagged categories with score bars and an + * override action for false positives. + * + * @param {Object} props Component props. + * @param {?Object} props.post Post with a `review` scores object. + * @param {string} props.type onProcessData type ('core'/'knowledge'). + * @param {Function} props.onClose Close handler. + * @param {Function} props.onSuccess Called after a successful override. + */ +const ModerationModal = ( { post, type = '', onClose, onSuccess } ) => { + const [ isBusy, setBusy ] = useState( false ); + + if ( ! post?.review ) { + return null; + } + + const onOverride = async () => { + setBusy( true ); + + await onProcessData( { + post, + type, + params: { + action: 'override', + }, + onSuccess: () => { + onClose(); + onSuccess(); + }, + } ); + + setBusy( false ); + }; + + return ( + +

+ { __( + 'The content of the post listed here could not be added or updated due to non-compliance with content policies. Review these to understand the limitations and possibly modify content to align with required standards.', + 'hyve-lite' + ) } +

+ +

+ { __( 'The following content was flagged for:', 'hyve-lite' ) } +

+ +
+ { Object.keys( post.review ).map( ( category ) => { + const percent = Math.floor( post.review[ category ] * 100 ); + + return ( +
+ + { moderationLabels[ category ]?.label ?? + category } +
+ ); + } ) } +
+ +

+ { __( + "Occasionally, OpenAI's Moderation system may incorrectly flag content as a violation; these are false positives. Such errors can occur because automated systems sometimes lack the necessary context to interpret nuances accurately. If your content is flagged but you believe it adheres to the guidelines, please manually review it. Should you determine it does not violate the content policies, you can click the button below to override the moderation decision.", + 'hyve-lite' + ) } +

+ +
+ + +
+
+ ); +}; + +export default ModerationModal; diff --git a/src/backend/parts/Notices.js b/src/backend/components/Notices.js similarity index 55% rename from src/backend/parts/Notices.js rename to src/backend/components/Notices.js index da8d9ff0..6e8dac48 100644 --- a/src/backend/parts/Notices.js +++ b/src/backend/components/Notices.js @@ -5,18 +5,14 @@ import { SnackbarList } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; -import { store as noticesStore } from '@wordpress/notices'; - const Notices = () => { - const notices = useSelect( - ( select ) => - select( noticesStore ) - .getNotices() - .filter( ( notice ) => 'snackbar' === notice.type ), - [] + const notices = useSelect( ( select ) => + select( 'core/notices' ) + .getNotices() + .filter( ( notice ) => 'snackbar' === notice.type ) ); - const { removeNotice } = useDispatch( noticesStore ); + const { removeNotice } = useDispatch( 'core/notices' ); return ( { + if ( 0 === page && ! hasMore ) { + return null; + } + + return ( +
+ + + { sprintf( + /* translators: 1: current page number, 2: total number of pages. */ + __( 'Page %1$s of %2$s', 'hyve-lite' ), + page + 1, + totalPages + ) } + + +
+ ); +}; + +export default Pagination; diff --git a/src/backend/components/ProgressBar.js b/src/backend/components/ProgressBar.js deleted file mode 100644 index 11ac626d..00000000 --- a/src/backend/components/ProgressBar.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * External dependencies. - */ -import classnames from 'classnames'; - -const ProgressBar = ( { value, max = 100, className } ) => { - const progress = 0 < max ? Math.round( ( value / max ) * 100 ) : 0; - - const wrapClasses = classnames( - 'w-full h-5 border rounded-md border-solid bg-white border-light-gray relative overflow-hidden', - className - ); - - return ( -
-
-
- ); -}; - -export default ProgressBar; diff --git a/src/backend/components/ServiceErrors.js b/src/backend/components/ServiceErrors.js new file mode 100644 index 00000000..83824e8e --- /dev/null +++ b/src/backend/components/ServiceErrors.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import { useSelect } from '@wordpress/data'; + +/** + * Persistent service-error notices (OpenAI/Qdrant outages, expired keys, + * missing credits). Seeded from the page load; the shared apiFetch middleware + * refreshes the store on every settings save. + */ +const ServiceErrors = () => { + const serviceErrors = useSelect( + ( select ) => select( 'hyve' ).getServiceErrors(), + [] + ); + + if ( ! serviceErrors?.length ) { + return null; + } + + const instructionMessage = + __( 'Please test the chat after solving the problem.', 'hyve-lite' ) + + ' ' + + __( + 'The error will disappear after a successful interaction with the chat.', + 'hyve-lite' + ); + + return serviceErrors.map( ( { provider, date, message, code } ) => ( +
+
+ + { sprintf( + /* translators: %s: the external service reporting the error, e.g. OpenAI. */ + __( '[%s] Service Error:', 'hyve-lite' ), + provider + ) }{ ' ' } + { message } ({ code }) + +

+ { new Date( date ).toLocaleString() } + { ' · ' } + { instructionMessage } +

+
+
+ ) ); +}; + +export default ServiceErrors; diff --git a/src/backend/components/SetupChecklist.js b/src/backend/components/SetupChecklist.js new file mode 100644 index 00000000..5ede5482 --- /dev/null +++ b/src/backend/components/SetupChecklist.js @@ -0,0 +1,145 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import { Button, Icon } from '@wordpress/components'; + +import { useSelect } from '@wordpress/data'; + +import { applyFilters } from '@wordpress/hooks'; + +import { check } from '@wordpress/icons'; + +/** + * Internal dependencies. + */ +import { navigate } from '../router'; + +const SetupChecklist = () => { + const { hasAPI, chunks } = useSelect( ( select ) => ( { + hasAPI: select( 'hyve' ).hasAPI(), + chunks: select( 'hyve' ).getTotalChunks(), + } ) ); + + const totalChunks = Number( chunks ?? 0 ); + + /** + * Setup steps. Pro prepends its license step (and locks the rest while + * the license is inactive) through the same filter. + */ + const steps = applyFilters( 'hyve.setup-steps', [ + { + title: __( 'Connect OpenAI', 'hyve-lite' ), + description: __( + 'Hyve uses your OpenAI API key to index content and answer visitors.', + 'hyve-lite' + ), + done: hasAPI, + locked: false, + action: { + label: __( 'Add API key', 'hyve-lite' ), + onClick: () => navigate( 'settings', 'ai-provider' ), + }, + }, + { + title: __( 'Add content to the Knowledge Base', 'hyve-lite' ), + description: __( + 'The chat can only answer from content you add. Start with your most useful pages.', + 'hyve-lite' + ), + done: 0 < totalChunks, + locked: ! hasAPI, + action: { + label: __( 'Add content', 'hyve-lite' ), + onClick: () => navigate( 'kb' ), + }, + }, + ] ); + + const required = steps.filter( ( step ) => ! step.optional ); + const doneCount = required.filter( ( step ) => step.done ).length; + const firstIncomplete = steps.findIndex( ( step ) => ! step.done ); + + return ( +
+
+

{ __( "Let's get Hyve running", 'hyve-lite' ) }

+ + { sprintf( + /* translators: 1: completed steps, 2: total required steps. */ + __( '%1$d of %2$d steps done', 'hyve-lite' ), + doneCount, + required.length + ) } + +
+ + { steps.map( ( step, index ) => ( +
+ + { step.done ? ( + <> + + + { __( 'Done', 'hyve-lite' ) } + + + ) : ( + index + 1 + ) } + +
+ + { step.title } + { step.optional && ( + + { __( 'Optional', 'hyve-lite' ) } + + ) } + +

{ step.description }

+
+ { ! step.done && ( + + ) } +
+ ) ) } + +

+ { __( + 'This checklist stays on your dashboard until the required steps are done.', + 'hyve-lite' + ) } +

+
+ ); +}; + +export default SetupChecklist; diff --git a/src/backend/components/SideNav.js b/src/backend/components/SideNav.js new file mode 100644 index 00000000..6a0dac0e --- /dev/null +++ b/src/backend/components/SideNav.js @@ -0,0 +1,57 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { useSelect } from '@wordpress/data'; + +import { Fragment } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import { navigate } from '../router'; + +const SideNav = ( { screen, subs, active } ) => { + const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); + + let lastGroup = null; + + return ( + + ); +}; + +export default SideNav; diff --git a/src/backend/components/Slot.js b/src/backend/components/Slot.js new file mode 100644 index 00000000..0c3a9551 --- /dev/null +++ b/src/backend/components/Slot.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies. + */ +import { applyFilters } from '@wordpress/hooks'; + +/** + * Named extension slot, filled through the `hyve.slot` filter. Whole panels + * swap through `component` on `hyve.routes` entries instead. + * + * @param {Object} props Component props. + * @param {string} props.name Slot name, e.g. 'api-access-tokens'. + * @param {?Object} props.fallback Rendered when nothing fills the slot. + * @param {Object} props.props Extra props passed to the filter. + */ +const Slot = ( { name, fallback = null, ...props } ) => + applyFilters( 'hyve.slot', fallback, name, props ); + +export default Slot; diff --git a/src/backend/components/StatCard.js b/src/backend/components/StatCard.js new file mode 100644 index 00000000..b303c9e4 --- /dev/null +++ b/src/backend/components/StatCard.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies. + */ +import { Icon } from '@wordpress/components'; + +const StatCard = ( { + icon, + label, + value, + suffix, + meter, + foot, + chip, + planned = false, +} ) => { + return ( +
+
+ { icon && } + { label } + { chip } +
+ +
+ { value } + { suffix && { suffix } } +
+ + { undefined !== meter && ( + + ) } + + { foot &&
{ foot }
} +
+ ); +}; + +export default StatCard; diff --git a/src/backend/components/TabNav.js b/src/backend/components/TabNav.js new file mode 100644 index 00000000..847c88f1 --- /dev/null +++ b/src/backend/components/TabNav.js @@ -0,0 +1,50 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { Icon } from '@wordpress/components'; + +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies. + */ +import { getAccessibleRoutes, navigate, useRoute } from '../router'; + +const TabNav = () => { + const { screen } = useRoute(); + + const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); + + const routes = getAccessibleRoutes(); + + return ( + + ); +}; + +export default TabNav; diff --git a/src/backend/components/UsageChart.js b/src/backend/components/UsageChart.js index 92d05de8..9b63ec1b 100644 --- a/src/backend/components/UsageChart.js +++ b/src/backend/components/UsageChart.js @@ -3,184 +3,136 @@ */ import { Chart, - BarController, CategoryScale, + Filler, + Legend, LinearScale, - BarElement, + LineController, + LineElement, + PointElement, Tooltip, - Legend, } from 'chart.js'; /** * WordPress dependencies. */ -import { useState, useEffect, useRef, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { SelectControl } from '@wordpress/components'; +import { useEffect, useRef } from '@wordpress/element'; Chart.register( - BarController, CategoryScale, + Filler, + Legend, LinearScale, - BarElement, - Tooltip, - Legend + LineController, + LineElement, + PointElement, + Tooltip ); -const UsageChart = ( { - id, - legendLabel, - data, - labels, - datasetBackgroundColor, - datasetBorderColor, -} ) => { +const UsageChart = ( { labels, messages, sessions } ) => { + const canvasRef = useRef( null ); const chartRef = useRef( null ); - const [ chartInstance, setChartInstance ] = useState( null ); - // Effect for creating and destroying the chart instance useEffect( () => { - if ( ! chartRef.current ) { + if ( ! canvasRef.current ) { return; } - const ctx = chartRef.current.getContext( '2d' ); - const newChart = new Chart( ctx, { - type: 'bar', + const instance = new Chart( canvasRef.current.getContext( '2d' ), { + type: 'line', data: { labels: [], datasets: [ { - label: legendLabel, + label: __( 'Messages', 'hyve-lite' ), data: [], - backgroundColor: datasetBackgroundColor, - borderColor: datasetBorderColor, - borderWidth: 1, + borderColor: '#2271b1', + backgroundColor: 'rgba(34, 113, 177, 0.08)', + fill: true, + tension: 0.3, + pointRadius: 0, + borderWidth: 2, + }, + { + label: __( 'Sessions', 'hyve-lite' ), + data: [], + borderColor: '#dba617', + backgroundColor: 'transparent', + fill: false, + tension: 0.3, + pointRadius: 0, + borderWidth: 2, }, ], }, options: { responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false, + }, + plugins: { + legend: { + position: 'top', + align: 'end', + labels: { + boxWidth: 12, + boxHeight: 12, + }, + }, + }, scales: { x: { ticks: { - maxTicksLimit: 15, + maxTicksLimit: 12, }, }, y: { beginAtZero: true, + ticks: { + precision: 0, + }, }, }, }, } ); - setChartInstance( newChart ); + + chartRef.current = instance; return () => { - if ( newChart ) { - newChart.destroy(); - } - setChartInstance( null ); + instance.destroy(); + chartRef.current = null; }; - }, [ legendLabel, datasetBackgroundColor, datasetBorderColor ] ); + }, [] ); - // Effect for updating chart data when dateRange or chart prop changes useEffect( () => { - if ( chartInstance && labels ) { - chartInstance.data.labels = labels; - chartInstance.data.datasets[ 0 ].data = data; - - if ( - legendLabel && - chartInstance.data.datasets[ 0 ].label !== legendLabel - ) { - chartInstance.data.datasets[ 0 ].label = legendLabel; - } + const instance = chartRef.current; - chartInstance.update(); - } else if ( chartInstance ) { - chartInstance.data.labels = []; - chartInstance.data.datasets[ 0 ].data = []; - if ( legendLabel ) { - chartInstance.data.datasets[ 0 ].label = legendLabel; - } - chartInstance.update(); + if ( ! instance ) { + return; } - }, [ chartInstance, legendLabel, labels, data ] ); - - return ( -
- -
- ); -}; - -export const UsageCharts = ( { chart } ) => { - const [ dateRange, setDateRange ] = useState( 30 ); - const filteredLabels = useMemo( () => { - return chart.labels.slice( -dateRange ); - }, [ chart.labels, dateRange ] ); - - const filteredMessageData = useMemo( () => { - return chart.data.messages.slice( -dateRange ); - }, [ chart.data.messages, dateRange ] ); - - const filteredSessionsData = useMemo( () => { - return chart.data.sessions.slice( -dateRange ); - }, [ chart.data.sessions, dateRange ] ); + instance.data.labels = labels; + instance.data.datasets[ 0 ].data = messages; + instance.data.datasets[ 1 ].data = sessions; + instance.update(); + }, [ labels, messages, sessions ] ); return ( -
-
- { - setDateRange( value ); - window.hyveTrk?.add?.( { - feature: 'charts', - featureComponent: 'days-filter', - featureValue: value, - } ); - } } - labelPosition="side" - /> -
- - +
+
); }; + +export default UsageChart; diff --git a/src/backend/data/useSaveSettings.js b/src/backend/data/useSaveSettings.js new file mode 100644 index 00000000..c91c9719 --- /dev/null +++ b/src/backend/data/useSaveSettings.js @@ -0,0 +1,60 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import apiFetch from '@wordpress/api-fetch'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { useState } from '@wordpress/element'; + +/** + * Save the current settings from the `hyve` store. + * + * @return {{settings: Object, isSaving: boolean, save: Function}} `save` + * resolves with the endpoint response, or null when the request failed. + */ +const useSaveSettings = () => { + const [ isSaving, setIsSaving ] = useState( false ); + + const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); + + const { createNotice } = useDispatch( 'core/notices' ); + + const save = async () => { + setIsSaving( true ); + + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/settings`, + method: 'POST', + data: { data: settings }, + } ); + + if ( response.error ) { + throw new Error( response.error ); + } + + createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { + type: 'snackbar', + isDismissible: true, + } ); + + return response; + } catch ( error ) { + createNotice( 'error', error.message, { + type: 'snackbar', + isDismissible: true, + } ); + + return null; + } finally { + setIsSaving( false ); + } + }; + + return { settings, isSaving, save }; +}; + +export default useSaveSettings; diff --git a/src/backend/index.js b/src/backend/index.js index 57d7b748..4289ed03 100644 --- a/src/backend/index.js +++ b/src/backend/index.js @@ -1,44 +1,45 @@ /** * WordPress dependencies. */ +import apiFetch from '@wordpress/api-fetch'; + import domReady from '@wordpress/dom-ready'; +import { dispatch } from '@wordpress/data'; + import { createRoot } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies. */ -import './style.scss'; import './store'; import App from './App'; -import { PostsTable } from './parts/PostsTable'; -import PostModal from './parts/PostModal'; import { getChatIcons } from './utils'; -import { OthersSection } from './parts/data/OthersSection'; -window.hyveComponents = {}; +// Keep the dashboard service-error notice in sync with every settings save, +// regardless of which component (lite or pro) triggers it. The settings +// endpoint returns the freshly-computed service errors on each save. +apiFetch.use( async ( options, next ) => { + const response = await next( options ); + + if ( + 'POST' === options.method && + 'string' === typeof options.path && + options.path.includes( '/settings' ) && + response && + Array.isArray( response.serviceErrors ) + ) { + dispatch( 'hyve' ).setServiceErrors( response.serviceErrors ); + } -window.hyveComponents.PostsTable = PostsTable; -window.hyveComponents.PostModal = PostModal; + return response; +} ); domReady( () => { addFilter( 'hyve.appearance.chat-icons', 'hyve/data', getChatIcons ); setUpTracking(); - addFilter( - 'hyve.others', - 'hyve/others', - ( el, isSaving, settings, setSetting, onSave ) => ( - - ) - ); - const root = createRoot( document.getElementById( 'hyve-options' ) ); root.render( ); } ); diff --git a/src/backend/parts/ErrorSection.js b/src/backend/parts/ErrorSection.js deleted file mode 100644 index a042680c..00000000 --- a/src/backend/parts/ErrorSection.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * WordPress dependencies. - */ - -import { Notice } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; - -export const ErrorSection = () => { - const instructionMessage = - __( 'Please test the chat after solving the problem.', 'hyve-lite' ) + - ' ' + - __( - 'The error will disappear after a successful interaction with the chat.', - 'hyve-lite' - ); - - return ( - <> - { window.hyve?.serviceErrors?.map( - ( { provider, date, message, code } ) => { - return ( - - - [{ provider }]{ ' ' } - { __( 'Service Error', 'hyve-lite' ) }: - { ' ' } - { message } ({ code }) |{ ' ' } - { new Date( date ).toLocaleString() } -
-
- { instructionMessage } -
- ); - } - ) } - - ); -}; diff --git a/src/backend/parts/Home.js b/src/backend/parts/Home.js deleted file mode 100644 index 6b3bf062..00000000 --- a/src/backend/parts/Home.js +++ /dev/null @@ -1,347 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { - Button, - Panel, - PanelRow, // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControl as ToggleGroupControl, - // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControlOption as ToggleGroupControlOption, -} from '@wordpress/components'; - -import apiFetch from '@wordpress/api-fetch'; - -import { dispatch, useDispatch, useSelect } from '@wordpress/data'; - -import { archive, brush, help } from '@wordpress/icons'; -import { UsageCharts } from '../components/UsageChart'; - -const { setRoute: changeRoute } = dispatch( 'hyve' ); - -import { useState, useCallback, useEffect } from '@wordpress/element'; - -const STATUS = [ - { - label: __( 'Sessions', 'hyve-lite' ), - value: window.hyve?.stats?.threads, - description: __( - 'Unique chat sessions created by your users.', - 'hyve-lite' - ), - }, - { - label: __( 'Messages', 'hyve-lite' ), - value: window.hyve?.stats?.messages, - description: __( - 'Total messages exchanged between users and Hyve.', - 'hyve-lite' - ), - }, - { - label: __( 'Knowledge Base', 'hyve-lite' ), - value: Boolean( window.hyve.isQdrantActive ) - ? window.hyve?.stats?.totalChunks - : `${ window.hyve?.stats?.totalChunks } / ${ window.hyve?.chunksLimit }`, - description: __( 'Current knowledge base chunks used.', 'hyve-lite' ), - action: { - label: __( 'Need more storage?', 'hyve-lite' ), - action: () => changeRoute( 'integrations' ), - condition: - ! Boolean( window.hyve.isQdrantActive ) && - 400 < window.hyve?.stats?.totalChunks, - }, - }, -]; - -const Dashboard = ( { isBlocked } ) => { - const { setRoute } = useDispatch( 'hyve' ); - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const { setSetting } = useDispatch( 'hyve' ); - const { createNotice } = useDispatch( 'core/notices' ); - - const [ autoUpdate, setAutoUpdate ] = useState( false ); - - const onSave = useCallback( async () => { - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - }, [ settings, createNotice ] ); - - useEffect( () => { - if ( autoUpdate ) { - onSave(); - setAutoUpdate( false ); - } - }, [ autoUpdate, onSave ] ); - - const ACTIONS = [ - { - id: 'knowledge-base', - label: __( 'Knowledge Base', 'hyve-lite' ), - description: __( - "In Knowledge Base, view and remove any Posts/Pages added to Hyve from the chat's data source.", - 'hyve-lite' - ), - icon: archive, - action: () => setRoute( 'data' ), - }, - { - id: 'settings', - label: __( 'Personalize', 'hyve-lite' ), - description: __( - "Customize Hyve's behavior and appearance to better suit your website and brand.", - 'hyve-lite' - ), - icon: brush, - action: () => setRoute( 'settings' ), - }, - { - id: 'docs', - label: __( 'Need help?', 'hyve-lite' ), - description: __( - 'Check out our documentation or contact support for assistance.', - 'hyve-lite' - ), - icon: help, - action: () => window?.open( window?.hyve?.docs, '_blank' ), - }, - ]; - - return ( -
- - - { - setSetting( 'chat_enabled', Boolean( newValue ) ); - setAutoUpdate( true ); - } } - help={ - __( - 'Display the Chat on all the pages.', - 'hyve-lite' - ) + - ' ' + - __( - 'For specific pages, use the Hyve Gutenberg Block.', - 'hyve-lite' - ) - } - > - - - - - { 0 === - Number( window.hyve?.stats?.totalChunks ?? '0' ) && ( -
-

- { __( - 'Your Knowledge Base is currently empty.', - 'hyve-lite' - ) }{ ' ' } - { __( - 'The Chat won’t be able to respond to questions until sources are added.', - 'hyve-lite' - ) }{ ' ' } - -

-
- ) } - -

- { __( 'Overview', 'hyve-lite' ) } -

- -
- { STATUS.map( - ( { label, value, description, action } ) => ( -
-
-
-
- { label } -
- -
- { value } -
- -
- { description } -
- - { action && action?.condition && ( - - ) } -
-
-
- ) - ) } -
- -

- { __( 'Get Started', 'hyve-lite' ) } -

- -
- { ACTIONS.map( - ( { id, label, description, icon, action } ) => ( - - ) - ) } -
-
-
- { ! isBlocked && 0 < window.hyve?.chart?.data?.messages?.length && ( - <> -
- - - - - - - ) } -
- ); -}; - -const Home = () => { - const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); - - const { setRoute } = useDispatch( 'hyve' ); - - if ( ! hasAPI ) { - return ( -
- - -
-
-

- { __( - 'Welcome to Hyve! Designed to seamlessly integrate AI chat into your WordPress site, this plugin allows you to craft a personalized chat experience using your own posts and pages. Enjoy engaging with your website visitors through Hyve!', - 'hyve-lite' - ) } -

- -

- { __( - "To begin using the Hyve plugin, you'll need an OpenAI API key. This key enables Hyve to communicate with OpenAI's powerful language models, ensuring you get the best possible responses.", - 'hyve-lite' - ) } -

- -
- - - -
-
-
-
- ); - } - - return ; -}; - -export default Home; diff --git a/src/backend/parts/Messages.js b/src/backend/parts/Messages.js deleted file mode 100644 index ecc5eb62..00000000 --- a/src/backend/parts/Messages.js +++ /dev/null @@ -1,349 +0,0 @@ -/** - * External dependencies. - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - Button, - Icon, - Modal, - Panel, - PanelRow, - Spinner, -} from '@wordpress/components'; - -import { useEffect, useState, useCallback } from '@wordpress/element'; - -import { applyFilters } from '@wordpress/hooks'; - -import { addQueryArgs } from '@wordpress/url'; - -import { useDispatch } from '@wordpress/data'; - -/** - * Internal dependencies. - */ -import UpsellContainer from './UpsellContainer'; - -import { formatDate } from '../utils'; - -const LoadMore = ( { onClick } ) => ( - -); - -const UpsellModalComponent = ( { isOpen, onRequestClose } ) => { - if ( ! isOpen ) { - return null; - } - return ( - - - { - - - ); -}; - -const ChatMessageBubble = ( { message } ) => { - const date = formatDate( message.time * 1000 ); - - if ( 'bot' === message.sender ) { - return ( -
-

- -

- ); - } - - if ( 'user' === message.sender ) { - return ( -
-

- { message.message } -

- -
- ); - } - - return null; -}; - -const MessageThreadView = ( { selectedPost, onDelete } ) => { - if ( ! selectedPost ) { - return ( -
-

- { __( - 'Select a message to view the conversation', - 'hyve-lite' - ) } -

-
- ); - } - - return ( - <> -
-

- { __( 'Thread ID', 'hyve-lite' ) } -

-

- { selectedPost?.thread_id?.replace( 'thread_', '' ) } -

- -
-
- { selectedPost?.thread?.map( ( message, index ) => ( - - ) ) } -
- - ); -}; - -const MessageListItem = ( { post, onClick, isSelected, isFirst } ) => ( - -); - -const MessageList = ( { - posts, - selectedPost, - onPostSelect, - loadMoreButton, -} ) => { - return ( -
- { posts?.map( ( post, index ) => ( - - ) ) } - { loadMoreButton } -
- ); -}; - -const ExportMessagesAction = ( { onClick } ) => { - return applyFilters( - 'hyve.messages.export-messages', - , - , - __( 'Export Messages', 'hyve-lite' ) - ); -}; - -const Messages = () => { - const [ posts, setPosts ] = useState( [] ); - const [ selectedPost, setSelectedPost ] = useState( null ); - const [ hasMore, setHasMore ] = useState( false ); - const [ isUpsellOpen, setUpsellOpen ] = useState( false ); - const [ isLoading, setLoading ] = useState( true ); - const [ offset, setOffset ] = useState( 0 ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const deleteConversation = useCallback( - async ( threadId ) => { - try { - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/threads`, { - id: threadId, - } ), - method: 'DELETE', - } ); - - if ( response.data ) { - setPosts( ( prev ) => - prev.filter( ( post ) => post.ID !== threadId ) - ); - setSelectedPost( null ); - createNotice( 'success', response.data, { - type: 'snackbar', - isDismissible: true, - } ); - - window.hyveTrk?.add?.( { - feature: 'dashboard', - featureComponent: 'messages-tab', - featureValue: 'delete-thread', - } ); - } - } catch ( error ) { - createNotice( 'error', error?.message, { - type: 'snackbar', - isDismissible: true, - } ); - } - }, - [ createNotice ] - ); - - useEffect( () => { - const fetchPosts = async () => { - setLoading( true ); - - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/threads`, { - offset, - } ), - } ); - - setLoading( false ); - setPosts( ( prev ) => [ ...prev, ...response.posts ] ); - setHasMore( response.more ); - }; - - fetchPosts(); - }, [ offset ] ); - - const loadMoreButton = hasMore - ? applyFilters( - 'hyve.messages.load-more', - setUpsellOpen( true ) } />, - isLoading, - () => { - setOffset( posts.length ); - } - ) - : null; - - return ( -
- setUpsellOpen( false ) } - /> - - - -

- { __( - 'Here you can see an history of all the messages between Hyve and your users.', - 'hyve-lite' - ) } -

- - { isLoading && ! posts?.length && ( -
- -
- ) } - - { ! isLoading && ! posts?.length && ( -
-

- { __( - 'Messsages between Hyve and your users will appear here', - 'hyve-lite' - ) } -

-
- ) } - - { ( ( ! isLoading && posts && 0 < posts.length ) || - ( posts && 0 < posts.length ) ) && ( - <> -
- { ( ! isLoading || - ( posts && 0 < posts.length ) ) && ( - - ) } - -
- -
-
-
- setUpsellOpen( true ) } - /> -
- - ) } -
-
-
- ); -}; - -export default Messages; diff --git a/src/backend/parts/ModerationReview.js b/src/backend/parts/ModerationReview.js deleted file mode 100644 index 5b8b7858..00000000 --- a/src/backend/parts/ModerationReview.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __, sprintf } from '@wordpress/i18n'; - -import { Button, Modal } from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { info } from '@wordpress/icons'; - -/** - * Internal dependencies. - */ -import { moderationLabels, onProcessData } from '../utils'; - -const ModerationReview = ( { - post, - type = '', - isOpen, - onClose, - onSuccess = () => {}, -} ) => { - const [ isBusy, setBusy ] = useState( false ); - - const onOverride = async () => { - setBusy( true ); - - await onProcessData( { - post, - type, - params: { - action: 'override', - }, - onSuccess: () => { - onClose(); - onSuccess(); - }, - } ); - - setBusy( false ); - }; - - if ( ! isOpen || ! post?.review ) { - return null; - } - - return ( - -

- { __( - 'The content of the post listed here could not be added or updated due to non-compliance with content policies. Review these to understand the limitations and possibly modify content to align with required standards.', - 'hyve-lite' - ) } -

- -

- { __( 'The following content was flagged for:', 'hyve-lite' ) } -

- - { post && - Object.keys( post.review ).map( ( review ) => ( -
-
-

- { moderationLabels[ review ].label } -

- -
- -
-
-
-
-
- -
- - { Math.floor( post.review[ review ] * 100 ) }% - -
-
- ) ) } - -

- { __( - "Occasionally, OpenAI's Moderation system may incorrectly flag content as a violation—these are false positives. Such errors can occur because automated systems sometimes lack the necessary context to interpret nuances accurately. If your content is flagged but you believe it adheres to the guidelines, please manually review it. Should you determine it does not violate the content policies, you can click the button below to override the moderation decision.", - 'hyve-lite' - ) } -

- -
- -
- - ); -}; - -export default ModerationReview; diff --git a/src/backend/parts/PostModal.js b/src/backend/parts/PostModal.js deleted file mode 100644 index 19081f87..00000000 --- a/src/backend/parts/PostModal.js +++ /dev/null @@ -1,183 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - Button, - Modal, - TextControl, - TextareaControl, -} from '@wordpress/components'; - -import { useDispatch } from '@wordpress/data'; - -import { useState } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import { onProcessData } from '../utils'; -import ModerationReview from './ModerationReview'; - -const PostModal = ( { action, post, onClose } ) => { - const [ data, setData ] = useState( { ...post } ); - const [ isLoading, setLoading ] = useState( false ); - const [ isModerationModalOpen, setModerationModalOpen ] = useState( false ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const isEdit = 'edit' === action; - - const onDelete = async () => { - setLoading( true ); - - const response = await apiFetch( { - path: `${ window.hyve.api }/knowledge/${ data.ID }`, - method: 'POST', - headers: { - 'X-HTTP-Method-Override': 'DELETE', - }, - } ); - - if ( response.error ) { - createNotice( 'error', response.error, { - type: 'snackbar', - isDismissible: true, - } ); - setLoading( false ); - - return; - } - - createNotice( 'success', __( 'Post has been deleted.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - - onClose( true ); - }; - - const onProcess = async () => { - setLoading( true ); - - await onProcessData( { - post: data, - type: 'knowledge', - onSuccess: () => { - onClose( true ); - setLoading( false ); - window.hyveTrk?.add?.( { - feature: 'knowledge-base', - featureComponent: 'add-data', - featureValue: 'import-custom-data', - } ); - }, - onError: ( error ) => { - if ( - 'content_failed_moderation' === error?.code && - undefined !== error.review - ) { - const newPost = { - ...data, - review: error.review, - }; - - setData( newPost ); - setModerationModalOpen( true ); - } - - setLoading( false ); - }, - } ); - }; - - if ( isModerationModalOpen ) { - return ( - { - setModerationModalOpen( false ); - } } - onSuccess={ () => { - onClose( true ); - setModerationModalOpen( false ); - } } - /> - ); - } - - return ( - onClose( false ) } - shouldCloseOnOverlayClick={ true } - isOpen={ true } - className="md:max-w-3xl md:w-full" - > -
- { - setData( { - ...data, - title, - } ); - } } - /> - - { - setData( { - ...data, - content, - } ); - } } - /> - -
- { isEdit && ( - - ) } - - -
-
-
- ); -}; - -export default PostModal; diff --git a/src/backend/parts/PostsTable.js b/src/backend/parts/PostsTable.js deleted file mode 100644 index 551983e0..00000000 --- a/src/backend/parts/PostsTable.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button, Spinner } from '@wordpress/components'; - -export const PostsTable = ( { - posts, - isLoading, - hasMore, - onFetch, - actions, -} ) => { - return ( - <> -
-
-
-
{ __( 'ID', 'hyve-lite' ) }
-
- { __( 'Title', 'hyve-lite' ) } -
-
- { __( 'Action', 'hyve-lite' ) } -
-
-
-
- { posts?.map( ( post ) => ( -
-
{ post.ID }
- -
- - { post.title } - -
- -
- { actions?.map( ( action ) => ( - - ) ) } -
-
- ) ) } - - { ! posts.length && ! isLoading && ( -
- { __( 'No data found.', 'hyve-lite' ) } -
- ) } -
-
- - { isLoading && ( -
- -
- ) } - - { hasMore && ( -
- -
- ) } - - ); -}; diff --git a/src/backend/parts/Sidebar.js b/src/backend/parts/Sidebar.js deleted file mode 100644 index 124764b6..00000000 --- a/src/backend/parts/Sidebar.js +++ /dev/null @@ -1,177 +0,0 @@ -/** - * External dependencies. - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -import { Button, Icon, Panel } from '@wordpress/components'; - -import { applyFilters } from '@wordpress/hooks'; - -import { chevronDown } from '@wordpress/icons'; - -/** - * Internal dependencies. - */ -import { ROUTE_TREE } from '../route'; -import { setUtm } from '../utils'; - -const Sidebar = () => { - const route = useSelect( ( select ) => select( 'hyve' ).getRoute() ); - const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); - - const { setRoute } = useDispatch( 'hyve' ); - - const MENU_ITEMS = applyFilters( 'hyve.route', ROUTE_TREE ); - - return ( -
- -
- -
-
- - { hasAPI && ! window?.hyve?.license && ( - <> -
- - -
-
-

- { __( 'Upgrade to Premium', 'hyve-lite' ) } -

-

- { __( - 'Unlock powerful features and enhance your chatbot experience.', - 'hyve-lite' - ) } -

-
- - -
-
- - ) } -
- ); -}; - -export default Sidebar; diff --git a/src/backend/parts/UpsellContainer.js b/src/backend/parts/UpsellContainer.js deleted file mode 100644 index 6dea8c0a..00000000 --- a/src/backend/parts/UpsellContainer.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import { setUtm } from '../utils'; - -const UpsellContainer = ( { title, description, campaign, children } ) => { - return ( -
- { children } - -
-
-
{ title }
-

{ description }

- - -
-
-
- ); -}; - -export default UpsellContainer; diff --git a/src/backend/parts/data/AddData.js b/src/backend/parts/data/AddData.js deleted file mode 100644 index 371da991..00000000 --- a/src/backend/parts/data/AddData.js +++ /dev/null @@ -1,252 +0,0 @@ -/** - * External dependencies. - */ -import hash from 'object-hash'; - -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - Button, - Notice, - Panel, - PanelRow, - SearchControl, - SelectControl, -} from '@wordpress/components'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -import { useEffect, useState, useMemo } from '@wordpress/element'; - -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies. - */ -import { onProcessData } from '../../utils'; - -import { PostsTable } from '../PostsTable'; - -import ModerationReview from '../ModerationReview'; - -const excludeTypes = [ 'attachment' ]; - -const postTypes = window.hyve.postTypes.filter( - ( postType ) => ! excludeTypes.includes( postType.value ) -); - -postTypes.unshift( { - label: __( 'All', 'hyve-lite' ), - value: 'any', -} ); - -const AddData = ( { refresh, setAddPost } ) => { - const [ posts, setPosts ] = useState( {} ); - const [ processedPosts, setProcessedPosts ] = useState( [] ); - const [ hasMore, setHasMore ] = useState( false ); - const [ isLoading, setLoading ] = useState( true ); - const [ isUpdating, setUpdating ] = useState( [] ); - const [ isModerationModalOpen, setModerationModalOpen ] = useState( false ); - const [ post, setPost ] = useState( null ); - const [ offset, setOffset ] = useState( 0 ); - - const [ query, setQuery ] = useState( { - type: 'any', - search: '', - } ); - const queryHash = useMemo( () => hash( query ), [ query ] ); - - const { setTotalChunks } = useDispatch( 'hyve' ); - const hasReachedLimit = useSelect( ( select ) => - select( 'hyve' ).hasReachedLimit() - ); - - const onProcess = async ( id ) => { - setUpdating( ( prev ) => [ ...prev, id ] ); - const currentPost = posts[ queryHash ].find( ( p ) => p.ID === id ); - - await onProcessData( { - post: currentPost, - onSuccess: () => { - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - setProcessedPosts( ( prev ) => [ ...prev, id ] ); - refresh(); - window.hyveTrk?.add?.( { - feature: 'knowledge-base', - featureComponent: 'add-data', - featureValue: 'import-wordpress-data', - } ); - }, - onError: ( error ) => { - if ( - 'content_failed_moderation' === error?.code && - undefined !== error.review - ) { - const newPost = { - ...currentPost, - review: error.review, - }; - - setPost( newPost ); - setModerationModalOpen( true ); - } - - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - }, - } ); - }; - - useEffect( () => { - const fetchData = async () => { - setLoading( true ); - - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/data`, { - offset, - ...query, - } ), - } ); - - setLoading( false ); - - setPosts( ( prev ) => ( { - ...prev, - [ queryHash ]: ( prev[ queryHash ] || [] ).concat( - response.posts - ), - } ) ); - - setHasMore( response.more ); - setTotalChunks( response?.totalChunks ); - }; - - const handler = setTimeout( () => fetchData(), 1000 ); - return () => clearTimeout( handler ); - }, [ query, setTotalChunks, queryHash, offset ] ); - - const onChangeQuery = ( key, value ) => { - setQuery( { - ...query, - [ key ]: value, - } ); - }; - - return ( - <> -
- -
-
- - - { hasReachedLimit && ( - - { __( - 'You have reached the limit of posts that can be added to the Knowledge Base. Please delete existing posts if you wish to add more.', - 'hyve-lite' - ) } - - ) } - -

- { __( - 'Select posts that are informative, engaging, and relevant. These will be the building blocks that empower your chat assistant to deliver precise and helpful responses. Whether it is answering FAQs or diving into detailed explanations, the content you choose here will shape how effectively your assistant interacts with users.', - 'hyve-lite' - ) } -

- -
-
-
- - onChangeQuery( 'type', e ) - } - /> -
- -
- - onChangeQuery( 'search', e ) - } - /> -
-
- - - ! processedPosts.includes( p.ID ) - ) || [] - } - isLoading={ isLoading } - hasMore={ hasMore } - onFetch={ () => { - setOffset( - posts[ queryHash ]?.length ?? 0 - ); - } } - actions={ [ - { - label: __( 'Add', 'hyve-lite' ), - isBusy: isUpdating, - onClick: onProcess, - isDisabled: hasReachedLimit, - }, - ] } - /> -
-
-
-
- - { - setModerationModalOpen( false ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - } } - onSuccess={ () => { - setModerationModalOpen( false ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - setProcessedPosts( ( prev ) => [ ...prev, post.ID ] ); - } } - /> - - ); -}; - -export default AddData; diff --git a/src/backend/parts/data/Custom.js b/src/backend/parts/data/Custom.js deleted file mode 100644 index 5fa0143a..00000000 --- a/src/backend/parts/data/Custom.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button, Panel, PanelRow, SearchControl } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import { PostsTable } from '../PostsTable'; -import UpsellContainer from '../UpsellContainer'; - -const posts = [ - { - ID: 1, - title: __( 'Halloween Limited Time Deal Information', 'hyve-lite' ), - }, - { - ID: 2, - title: __( 'What to do if my order is missing an item?', 'hyve-lite' ), - }, - { - ID: 3, - title: __( 'How do I return an item?', 'hyve-lite' ), - }, - { - ID: 4, - title: __( 'How do I track my order?', 'hyve-lite' ), - }, - { - ID: 5, - title: __( 'How do I change my delivery address?', 'hyve-lite' ), - }, - { - ID: 6, - title: __( 'How do I cancel my order?', 'hyve-lite' ), - }, -]; - -const Custom = ( { setView } ) => { - return ( -
- -
-
- - -

- { __( - 'Custom Data allows you to privately feed specific data directly into your chat bot without displaying this information on your public website. With this, you can equip your bot with unique, specialized knowledge that aligns with your business needs and customer queries.', - 'hyve-lite' - ) } -

- -
- -
-
- {} } - /> -
- -
- -
-
- - {} } - actions={ [ - { - label: __( 'Edit', 'hyve-lite' ), - isBusy: [], - onClick: () => {}, - isDisabled: false, - }, - ] } - /> -
-
-
-
-
- ); -}; - -export default Custom; diff --git a/src/backend/parts/data/FAQ.js b/src/backend/parts/data/FAQ.js deleted file mode 100644 index ca6fadd0..00000000 --- a/src/backend/parts/data/FAQ.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button, Panel, PanelRow } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import UpsellContainer from '../UpsellContainer'; - -const posts = [ - { - question: __( 'How do I reset my password?', 'hyve-lite' ), - count: 5, - }, - { - question: __( 'How do I change my email address?', 'hyve-lite' ), - count: 3, - }, - { - question: __( 'How do I update my payment method?', 'hyve-lite' ), - count: 2, - }, - { - question: __( 'How do I cancel my subscription?', 'hyve-lite' ), - count: 1, - }, - { - question: __( 'How do I change my plan?', 'hyve-lite' ), - count: 1, - }, - { - question: __( 'How do I update my billing information?', 'hyve-lite' ), - count: 1, - }, -]; - -const Custom = () => { - return ( -
- - -

- { __( - "The FAQ captures frequently asked questions that went unanswered by our chatbot, providing you with a valuable insight into what your users are seeking. This feature allows you to review these queries and decide whether to incorporate them into your bot's knowledge base. By actively updating your FAQ, you can continuously refine your chatbot's ability to address user needs effectively and enhance their interactive experience. These aren't updated instantly.", - 'hyve-lite' - ) } -

- -
- -
-
-
-
- { __( 'Title', 'hyve-lite' ) } -
-
- { __( 'Count', 'hyve-lite' ) } -
-
- { __( 'Action', 'hyve-lite' ) } -
-
-
-
- { posts?.map( ( post, key ) => ( -
-
- - { post.question } - -
- -
- { post.count } -
- -
- - - -
-
- ) ) } -
-
-
-
-
-
-
- ); -}; - -export default Custom; diff --git a/src/backend/parts/data/FailedModeration.js b/src/backend/parts/data/FailedModeration.js deleted file mode 100644 index e7e6e6c9..00000000 --- a/src/backend/parts/data/FailedModeration.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { Panel, PanelRow } from '@wordpress/components'; - -import { useDispatch } from '@wordpress/data'; - -import { useEffect, useState } from '@wordpress/element'; - -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies. - */ -import { onProcessData } from '../../utils'; -import { PostsTable } from '../PostsTable'; -import ModerationReview from '../ModerationReview'; - -const FailedModeration = () => { - const [ needsUpdate, setNeedsUpdate ] = useState( [] ); - const [ hasMoreUpdate, setHasMoreUpdate ] = useState( false ); - const [ isLoadingUpdate, setLoadingUpdate ] = useState( false ); - const [ isUpdating, setUpdating ] = useState( [] ); - const [ isOpen, setOpen ] = useState( false ); - const [ post, setPost ] = useState( null ); - const [ offset, setOffset ] = useState( 0 ); - - const { setTotalChunks } = useDispatch( 'hyve' ); - - const onUpdate = async ( id ) => { - setUpdating( ( prev ) => [ ...prev, id ] ); - const currentPost = needsUpdate.find( ( p ) => p.ID === id ); - - await onProcessData( { - post: currentPost, - params: { - action: 'update', - }, - onSuccess: () => { - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - setNeedsUpdate( ( prev ) => - prev.filter( ( p ) => p.ID !== id ) - ); - }, - onError: ( error ) => { - if ( - 'content_failed_moderation' === error?.code && - undefined !== error.review - ) { - const newPost = { - ...currentPost, - review: error.review, - }; - - setPost( newPost ); - setOpen( true ); - } - - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - }, - } ); - }; - - useEffect( () => { - const fetchUpdate = async () => { - setLoadingUpdate( true ); - - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/data`, { - offset, - status: 'moderation', - } ), - } ); - - setLoadingUpdate( false ); - setNeedsUpdate( ( prev ) => [ ...prev, ...response.posts ] ); - setHasMoreUpdate( response.more ); - setTotalChunks( response?.totalChunks ); - }; - - fetchUpdate(); - }, [ offset, setTotalChunks ] ); - - return ( - <> -
- - -

- { __( - "On this page, you'll find posts and pages that could not be added or updated due to non-compliance with content policies. Review these to understand the limitations and possibly modify content to align with required standards. You can also adjust the moderation threshold in the Settings.", - 'hyve-lite' - ) } -

- -
- { - setOffset( needsUpdate.length ); - } } - actions={ [ - { - label: __( 'More Info', 'hyve-lite' ), - isBusy: isUpdating, - variant: 'secondary', - onClick: ( ID ) => { - const currentPost = - needsUpdate.find( - ( p ) => p.ID === ID - ); - setOpen( true ); - setPost( currentPost ); - }, - }, - { - label: __( 'Update', 'hyve-lite' ), - isBusy: isUpdating, - onClick: onUpdate, - }, - ] } - /> -
-
-
-
- - { - setOpen( false ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - } } - onSuccess={ () => { - setNeedsUpdate( ( prev ) => - prev.filter( ( i ) => i.ID !== post.ID ) - ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - } } - /> - - ); -}; - -export default FailedModeration; diff --git a/src/backend/parts/data/KnowledgeBase.js b/src/backend/parts/data/KnowledgeBase.js deleted file mode 100644 index 547c4382..00000000 --- a/src/backend/parts/data/KnowledgeBase.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button, Panel, PanelRow } from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { applyFilters } from '@wordpress/hooks'; - -/** - * Internal dependencies. - */ -import { KNOWLEDGE_BASE } from '../../route'; -import { KnowledgeBaseOptions } from '../../components/KnowledgeBaseOptions'; - -const KnowledgeBase = () => { - const [ view, setView ] = useState( null ); - - const SOURCES = applyFilters( 'hyve.data', KNOWLEDGE_BASE ); - - if ( view ) { - const { component: Component } = SOURCES[ view ]; - return ; - } - - return ( -
- - -

- { __( - "A list of all the content that has been added to the Knowledge Base. It's the foundation that supports your chat assistant, enabling it to provide accurate and insightful responses.", - 'hyve-lite' - ) } -

- -
- { Object.keys( SOURCES ).map( ( key ) => { - const { - label, - description, - icon, - isPro = false, - } = SOURCES[ key ]; - return ( - - ); - } ) } -
-
-
-
- -
- ); -}; - -export default KnowledgeBase; diff --git a/src/backend/parts/data/OthersSection.js b/src/backend/parts/data/OthersSection.js deleted file mode 100644 index 1586cb08..00000000 --- a/src/backend/parts/data/OthersSection.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Panel, PanelRow, ToggleControl } from '@wordpress/components'; - -import { useState, useEffect } from '@wordpress/element'; - -export const OthersSection = ( { settings, setSetting, isSaving, onSave } ) => { - const [ isUpdated, setUpdated ] = useState( false ); - - useEffect( () => { - if ( ! isUpdated ) { - return; - } - - onSave(); - setUpdated( false ); - }, [ isUpdated, settings, onSave ] ); - - return ( -
- - - { - setSetting( 'telemetry_enabled', checked ); - setTimeout( () => { - setUpdated( true ); - }, 500 ); - } } - disabled={ isSaving || isUpdated } - /> - - -
- ); -}; diff --git a/src/backend/parts/data/Posts.js b/src/backend/parts/data/Posts.js deleted file mode 100644 index 4ea9b8a9..00000000 --- a/src/backend/parts/data/Posts.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { Button, Panel, PanelRow } from '@wordpress/components'; - -import { useDispatch } from '@wordpress/data'; - -import { useEffect, useState } from '@wordpress/element'; - -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies. - */ -import { PostsTable } from '../PostsTable'; -import AddData from './AddData'; - -const Posts = ( { setView } ) => { - const [ posts, setPosts ] = useState( [] ); - const [ hasMore, setHasMore ] = useState( false ); - const [ isLoading, setLoading ] = useState( true ); - const [ isDeleting, setDeleting ] = useState( [] ); - const [ addPost, setAddPost ] = useState( false ); - - const { createNotice } = useDispatch( 'core/notices' ); - const { setTotalChunks } = useDispatch( 'hyve' ); - - const [ offset, setOffset ] = useState( 0 ); - - const onDelete = async ( id ) => { - setDeleting( [ ...isDeleting, id ] ); - - await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/data`, { - id, - } ), - method: 'DELETE', - } ); - - setPosts( posts.filter( ( post ) => post.ID !== id ) ); - - createNotice( 'success', __( 'Post has been removed.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - }; - - useEffect( () => { - const fetchPosts = async () => { - setLoading( true ); - - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/data`, { - offset, - status: 'included', - } ), - } ); - - setLoading( false ); - setPosts( ( prev ) => [ ...prev, ...response.posts ] ); - setHasMore( response.more ); - setTotalChunks( response?.totalChunks ); - }; - - fetchPosts(); - }, [ offset, setTotalChunks ] ); - - if ( addPost ) { - return ( - { - setOffset( ( prev ) => prev + posts.length ); - } } - setAddPost={ setAddPost } - /> - ); - } - - return ( -
- -
-
- - -

- { __( - 'All the content from your WordPress site that has been added to the Knowledge Base.', - 'hyve-lite' - ) } -

- -
- -
- -
- { - setOffset( posts.length ); - } } - actions={ [ - { - label: __( 'Remove', 'hyve-lite' ), - isBusy: isDeleting, - variant: 'secondary', - isDestructive: true, - onClick: onDelete, - }, - ] } - /> -
-
-
-
- ); -}; - -export default Posts; diff --git a/src/backend/parts/data/SitemapCrawler.js b/src/backend/parts/data/SitemapCrawler.js deleted file mode 100644 index 14666eb8..00000000 --- a/src/backend/parts/data/SitemapCrawler.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -// eslint-disable-next-line import/no-extraneous-dependencies -import { capitalize } from 'lodash'; - -import { Button, Panel, PanelRow } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import UpsellContainer from '../UpsellContainer'; - -const posts = { - 1: { - url: 'https://example.com/sitemap.xml', - status: 'queued', - }, - 2: { - url: 'https://example.com/sitemap.xml', - status: 'completed', - }, - 3: { - url: 'https://example.com/sitemap.xml', - status: 'completed', - }, -}; - -const SitemapCrawler = ( { setView } ) => { - return ( - <> -
- -
-
- - -

- { __( - 'Use this tool to crawl a website and add its content to the Knowledge Base using the sitemap.', - 'hyve-lite' - ) } -

- -
- -
-
- -
-
- -
-
-
-
- { __( - 'Sitemap URL', - 'hyve-lite' - ) } -
-
- { __( 'Status', 'hyve-lite' ) } -
-
- { __( 'Action', 'hyve-lite' ) } -
-
-
- -
- { Object.keys( posts ).map( - ( post ) => ( -
-
- - { - posts[ post ] - .url - } - -
- -
- { capitalize( - posts[ post ].status - ) } -
- -
- -
-
- ) - ) } -
-
-
-
-
-
-
- - ); -}; - -export default SitemapCrawler; diff --git a/src/backend/parts/data/URLCrawler.js b/src/backend/parts/data/URLCrawler.js deleted file mode 100644 index ffb22b04..00000000 --- a/src/backend/parts/data/URLCrawler.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { Button, TextControl, Panel, PanelRow } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import { PostsTable } from '../PostsTable'; -import UpsellContainer from '../UpsellContainer'; - -const posts = Array.from( { length: 5 }, ( _, i ) => ( { - id: i + 1, - title: `https://example.com/page${ i + 1 }`, -} ) ); - -const URLCrawler = ( { setView } ) => { - return ( - <> -
- -
-
- - -

- { __( - 'This page allows you to add URLs to the Knowledge Base. You can add URLs to the Knowledge Base by entering the URL in the field below and clicking the "Crawl URL" button.', - 'hyve-lite' - ) } -

- -
- -
-
- {} } - /> - - -
-
- - {} } - actions={ [ - { - label: __( 'Delete', 'hyve-lite' ), - onClick: () => {}, - isBusy: [], - variant: 'secondary', - isDestructive: true, - }, - ] } - /> -
-
-
-
-
- - ); -}; - -export default URLCrawler; diff --git a/src/backend/parts/data/Updated.js b/src/backend/parts/data/Updated.js deleted file mode 100644 index 1faeaeac..00000000 --- a/src/backend/parts/data/Updated.js +++ /dev/null @@ -1,148 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { Panel, PanelRow } from '@wordpress/components'; - -import { useDispatch } from '@wordpress/data'; - -import { useEffect, useState } from '@wordpress/element'; - -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies. - */ -import { onProcessData } from '../../utils'; -import { PostsTable } from '../PostsTable'; -import ModerationReview from '../ModerationReview'; - -const Updated = () => { - const [ needsUpdate, setNeedsUpdate ] = useState( [] ); - const [ hasMoreUpdate, setHasMoreUpdate ] = useState( false ); - const [ isLoadingUpdate, setLoadingUpdate ] = useState( false ); - const [ isUpdating, setUpdating ] = useState( [] ); - const [ isModerationModalOpen, setModerationModalOpen ] = useState( false ); - const [ post, setPost ] = useState( null ); - const [ offset, setOffset ] = useState( 0 ); - - const { setTotalChunks } = useDispatch( 'hyve' ); - - const onUpdate = async ( id ) => { - setUpdating( ( prev ) => [ ...prev, id ] ); - const currentPost = needsUpdate.find( ( p ) => p.ID === id ); - - await onProcessData( { - post: currentPost, - params: { - action: 'update', - }, - onSuccess: () => { - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - setNeedsUpdate( ( prev ) => - prev.filter( ( p ) => p.ID !== id ) - ); - }, - onError: ( error ) => { - if ( - 'content_failed_moderation' === error?.code && - undefined !== error.review - ) { - const newPost = { - ...currentPost, - review: error.review, - }; - - setPost( newPost ); - setModerationModalOpen( true ); - } - - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== id ) - ); - }, - } ); - }; - - useEffect( () => { - const fetchUpdate = async () => { - setLoadingUpdate( true ); - - const response = await apiFetch( { - path: addQueryArgs( `${ window.hyve.api }/data`, { - offset, - status: 'pending', - } ), - } ); - - setLoadingUpdate( false ); - setNeedsUpdate( ( prev ) => [ ...prev, ...response.posts ] ); - setHasMoreUpdate( response.more ); - setTotalChunks( response?.totalChunks ); - }; - - fetchUpdate(); - }, [ setTotalChunks, offset ] ); - - return ( - <> -
- - -

- { __( - "Here, you'll see posts that have been updated since their addition to the Knowledge Base. This page allows you to review these updates and decide if you want to refresh the knowledge your assistant relies on.", - 'hyve-lite' - ) } -

- -
- { - setOffset( needsUpdate.length ); - } } - actions={ [ - { - label: __( 'Update', 'hyve-lite' ), - isBusy: isUpdating, - onClick: onUpdate, - }, - ] } - /> -
-
-
-
- - { - setModerationModalOpen( false ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - } } - onSuccess={ () => { - setModerationModalOpen( false ); - setUpdating( ( prev ) => - prev.filter( ( postId ) => postId !== post.ID ) - ); - setNeedsUpdate( ( prev ) => - prev.filter( ( item ) => item.ID !== post.ID ) - ); - } } - /> - - ); -}; - -export default Updated; diff --git a/src/backend/parts/integrations/ExternalSearch.js b/src/backend/parts/integrations/ExternalSearch.js deleted file mode 100644 index 4ab71b57..00000000 --- a/src/backend/parts/integrations/ExternalSearch.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; -import { Button, Panel, PanelRow } from '@wordpress/components'; -import { Fragment } from '@wordpress/element'; -import { applyFilters } from '@wordpress/hooks'; - -/** - * Internal dependencies. - */ -import { setUtm } from '../../utils'; - -export const ExternalSearch = () => { - return ( -
- - -

- { __( - 'Enable external services to search your Knowledge Base using advanced semantic search powered by Retrieval-Augmented Generation (RAG) and OpenAI embeddings.', - 'hyve-lite' - ) }{ ' ' } - { __( - 'Integrate automation tools (Zapier, n8n, etc.) with a secure API endpoint to perform intelligent content searches via simple API requests.', - 'hyve-lite' - ) } -

-

- { __( - 'With an Access Token, you can securely search your content from any location using the API.', - 'hyve-lite' - ) } -

-

- { [ - 'curl --request POST', - ` --url ${ window.hyve.rest_url }/knowledge-base/search`, - ` --header 'authorization: Bearer hyve_sk_MozAlDXXXXXXXXXXXXXXXX'`, - " --header 'content-type: application/json'", - " --data '{", - ' "query": "What is the cost of the phone?"', - "}'", - ].map( ( line, idx ) => ( - - { line } -
-
- ) ) } -

-
-
-
- { applyFilters( 'hyve.tokens-management', ) } -
- ); -}; - -const TokensManagement = () => { - return ( - - -

- { __( - 'Upgrade to Pro to unlock advanced access management: generate and manage secure API tokens, and control who can access your Knowledge Base via external integrations. Empower your team and automate workflows with confidence and security.', - 'hyve-lite' - ) } -

- -
-
- ); -}; diff --git a/src/backend/parts/integrations/Qdrant.js b/src/backend/parts/integrations/Qdrant.js deleted file mode 100644 index 40695f01..00000000 --- a/src/backend/parts/integrations/Qdrant.js +++ /dev/null @@ -1,289 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - Button, - ExternalLink, - Icon, - Modal, - Panel, - PanelRow, - TextControl, -} from '@wordpress/components'; - -import { useEffect, useState, useCallback } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -import { check } from '@wordpress/icons'; - -/** - * Internal dependencies. - */ -import ProgressBar from '../../components/ProgressBar'; - -const Qdrant = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const isQdrantActive = useSelect( ( select ) => - select( 'hyve' ).isQdrantActive() - ); - - const { setSetting, setQdrantStatus } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - const [ isOpen, setIsOpen ] = useState( false ); - const [ migrationStatus, setMigrationStatus ] = useState( [] ); - - const getQdrantStatus = useCallback( async () => { - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/qdrant`, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - setQdrantStatus( Boolean( response.status ) ); - setMigrationStatus( response.migration ); - - if ( response.migration?.in_progress ) { - setTimeout( getQdrantStatus, 10000 ); - } - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - }, [ setQdrantStatus, createNotice ] ); - - useEffect( () => { - getQdrantStatus(); - }, [ getQdrantStatus ] ); - - const onDeactivate = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/qdrant`, - method: 'POST', - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - setQdrantStatus( false ); - - createNotice( - 'success', - __( 'Qdrant disconnected.', 'hyve-lite' ), - { - type: 'snackbar', - isDismissible: true, - } - ); - window.hyveTrk?.add( { - feature: 'qdrant', - featureComponent: 'api-key', - featureValue: 'disconnected', - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - await getQdrantStatus(); - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - - window.hyveTrk?.add( { - feature: 'qdrant', - featureComponent: 'api-key', - featureValue: 'connected', - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - return ( -
- - { ! isQdrantActive && ( - <> - -

- { __( - "Use Qdrant to increase the Knowledge Base limit of Hyve. By integrating Qdrant, you can manage larger datasets and improve query performance for your website. To integrate Qdrant with your application, you'll need an API key and endpoint.", - 'hyve-lite' - ) } -

-

- { __( - 'Qdrant offers a free plan that supports thousands of data chunks, making it an excellent choice for most use cases without incurring additional costs.', - 'hyve-lite' - ) } -

- - - { __( 'Learn more about Qdrant', 'hyve-lite' ) } - -
- - - - setSetting( 'qdrant_api_key', newValue ) - } - /> - - - - - setSetting( 'qdrant_endpoint', newValue ) - } - /> - - - - - - - ) } - - { isQdrantActive && Boolean( migrationStatus?.in_progress ) && ( - -

- { __( - 'Migration in progress. Please wait while we process your data. This may take a couple of minutes.', - 'hyve-lite' - ) } -

- - -
- ) } - - { isQdrantActive && - ! Boolean( migrationStatus?.in_progress ) && ( - <> - { isOpen && ( - setIsOpen( false ) } - size="medium" - > -

- { __( - 'If you proceed, all the data associated with this website will be deleted from Qdrant, and all the posts exceeding the Knowledge Base limit will be removed from the Knowledge Base.', - 'hyve-lite' - ) } -

- - -
- ) } - - -

- - - { __( - 'Qdrant is connected and ready to use.', - 'hyve-lite' - ) } -

- - -
- - ) } -
-
- ); -}; - -export default Qdrant; diff --git a/src/backend/parts/settings/Advanced.js b/src/backend/parts/settings/Advanced.js deleted file mode 100644 index ff6a99b5..00000000 --- a/src/backend/parts/settings/Advanced.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - BaseControl, - Button, - ExternalLink, - Panel, - PanelRow, - TextControl, -} from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -import { applyFilters } from '@wordpress/hooks'; - -const Advanced = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - - const { setHasAPI, setSetting } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - if ( settings.api_key ) { - setHasAPI( true ); - } else { - setHasAPI( false ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - return ( -
- - - - { - window.hyveTrk?.add( { - feature: 'openai', - featureComponent: 'api-key', - featureValue: 'added', - } ); - setSetting( 'api_key', newValue ); - } } - /> - - - - { __( 'Get an API key', 'hyve-lite' ) } - - - - - -
- { applyFilters( - 'hyve.others', - () => ( - <> - ), - isSaving, - settings, - setSetting, - onSave - ) } -
- ); -}; - -export default Advanced; diff --git a/src/backend/parts/settings/Appearance.js b/src/backend/parts/settings/Appearance.js deleted file mode 100644 index 69e59221..00000000 --- a/src/backend/parts/settings/Appearance.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { - BaseControl, - Button, - ColorPalette, - Panel, - PanelRow, - // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControl as ToggleGroupControl, - // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, -} from '@wordpress/components'; - -import { applyFilters } from '@wordpress/hooks'; -import { useMemo } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import UpsellContainer from '../UpsellContainer'; - -const colorOptions = [ - { - label: __( 'Chat Background', 'hyve-lite' ), - value: 'chat_background', - default: '#ffffff', - }, - { - label: __( 'Assistant Background', 'hyve-lite' ), - value: 'assistant_background', - default: '#ecf1fb', - }, - { - label: __( 'User Background', 'hyve-lite' ), - value: 'user_background', - default: '#1155cc', - }, - { - label: __( 'Icon Background', 'hyve-lite' ), - value: 'icon_background', - default: '#1155cc', - }, -]; - -const Appearance = () => { - const chatIconOptions = useMemo( () => { - return applyFilters( 'hyve.appearance.chat-icons', [] ); - }, [] ); - - return ( -
- - - { 0 < chatIconOptions?.length && ( - - {} } - className="hyve-chat-icons" - > - { chatIconOptions?.map( - ( { icon, label, value } ) => { - const CustomIcon = icon; - return ( - - } - label={ label } - value={ value } - /> - ); - } - ) } - - - ) } - - { colorOptions.map( ( option ) => ( - - {} } - /> - - ) ) } - - - - - - - -
- ); -}; - -export default Appearance; diff --git a/src/backend/parts/settings/Assistant.js b/src/backend/parts/settings/Assistant.js deleted file mode 100644 index 384479cb..00000000 --- a/src/backend/parts/settings/Assistant.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - Button, - Panel, - PanelRow, - RangeControl, - RadioControl, -} from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -const Assistant = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const { setSetting } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - return ( -
- - - - setSetting( 'chat_model', newValue ) - } - /> - - - - - setSetting( 'temperature', newValue ) - } - /> - - - - - setSetting( 'top_p', newValue ) - } - /> - - - - - - -
- ); -}; - -export default Assistant; diff --git a/src/backend/parts/settings/General.js b/src/backend/parts/settings/General.js deleted file mode 100644 index 56cb1721..00000000 --- a/src/backend/parts/settings/General.js +++ /dev/null @@ -1,212 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { - BaseControl, - Button, - Panel, - PanelRow, - TextControl, - // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControl as ToggleGroupControl, - // eslint-disable-next-line @wordpress/no-unsafe-wp-apis - __experimentalToggleGroupControlOption as ToggleGroupControlOption, -} from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -import { applyFilters } from '@wordpress/hooks'; - -/** - * Internal dependencies. - */ -import UpsellContainer from '../UpsellContainer'; - -const SuggestedQuestions = () => { - return ( - - - -

- { __( - 'These questions will be displayed in the chat to get the conversation started.', - 'hyve-lite' - ) } -

- -
- { Array.from( { length: 3 } ).map( ( _, index ) => ( - {} } - /> - ) ) } -
-
-
-
- ); -}; - -const General = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const { setSetting } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - return ( -
- - - - setSetting( - 'post_row_addon_enabled', - Boolean( newValue ) - ) - } - help={ __( - 'When enabled, an “Add to Hyve” button will appear in the post/page row actions. Disable this to remove the option entirely from the posts/pages list.', - 'hyve-lite' - ) } - > - - - - - - - - setSetting( 'welcome_message', newValue ) - } - /> - - - - - setSetting( 'default_message', newValue ) - } - /> - - - { applyFilters( - 'hyve.suggestedQuestions', - , - isSaving, - settings, - setSetting - ) } - - - - - -
- ); -}; - -export default General; diff --git a/src/backend/parts/settings/Moderation.js b/src/backend/parts/settings/Moderation.js deleted file mode 100644 index 05b92393..00000000 --- a/src/backend/parts/settings/Moderation.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import apiFetch from '@wordpress/api-fetch'; - -import { Button, Panel, PanelRow, RangeControl } from '@wordpress/components'; - -import { useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; - -/** - * Internal dependencies. - */ -import { moderationLabels } from '../../utils'; - -const Moderation = () => { - const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); - const { setSetting } = useDispatch( 'hyve' ); - - const { createNotice } = useDispatch( 'core/notices' ); - - const [ isSaving, setIsSaving ] = useState( false ); - - const onSave = async () => { - setIsSaving( true ); - - try { - const response = await apiFetch( { - path: `${ window.hyve.api }/settings`, - method: 'POST', - data: { - data: settings, - }, - } ); - - if ( response.error ) { - throw new Error( response.error ); - } - - createNotice( 'success', __( 'Settings saved.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); - } catch ( error ) { - createNotice( 'error', error, { - type: 'snackbar', - isDismissible: true, - } ); - } - - setIsSaving( false ); - }; - - const onChangeModeration = ( key, value ) => { - setSetting( 'moderation_threshold', { - ...settings.moderation_threshold, - [ key ]: Number( value ) * 100, - } ); - }; - - return ( -
- - -

- { __( - 'With the moderation functionality, you can check whether your content is potentially harmful. Users can use it to identify content that might be harmful and take action.', - 'hyve-lite' - ) } -

- -

- { __( - "Occasionally, OpenAI's Moderation system may incorrectly flag content as a violation—these are false positives. Such errors can occur because automated systems sometimes lack the necessary context to interpret nuances accurately. If your content is flagged but you believe it adheres to the guidelines, please manually review it. Should you determine it does not violate the content policies, you can also override the moderation decisions.", - 'hyve-lite' - ) } -

- - { Object.keys( moderationLabels ).map( ( moderation ) => ( - - onChangeModeration( moderation, newValue ) - } - /> - ) ) } -
- - - - -
-
- ); -}; - -export default Moderation; diff --git a/src/backend/route.js b/src/backend/route.js deleted file mode 100644 index c45be4d1..00000000 --- a/src/backend/route.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; - -import { - archive, - blockMeta, - comment, - customLink, - home, - settings, - wordpress, -} from '@wordpress/icons'; - -/** - * Internal dependencies. - */ -import Home from './parts/Home'; -import KnowledgeBase from './parts/data/KnowledgeBase'; -import Posts from './parts/data/Posts'; -import Updated from './parts/data/Updated'; -import FailedModeration from './parts/data/FailedModeration'; -import Custom from './parts/data/Custom'; -import URLCrawler from './parts/data/URLCrawler'; -import SitemapCrawler from './parts/data/SitemapCrawler'; -import FAQ from './parts/data/FAQ'; -import Messages from './parts/Messages'; -import Qdrant from './parts/integrations/Qdrant'; -import General from './parts/settings/General'; -import Appearance from './parts/settings/Appearance'; -import Assistant from './parts/settings/Assistant'; -import Moderation from './parts/settings/Moderation'; -import Advanced from './parts/settings/Advanced'; -import { ExternalSearch } from './parts/integrations/ExternalSearch'; - -export const ROUTE_TREE = { - home: { - label: __( 'Dashboard', 'hyve-lite' ), - icon: home, - component: Home, - disabled: false, - }, - data: { - label: __( 'Knowledge Base', 'hyve-lite' ), - icon: archive, - children: { - data: { - label: __( 'Knowledge Base', 'hyve-lite' ), - component: KnowledgeBase, - }, - update: { - label: __( 'Requires Update', 'hyve-lite' ), - component: Updated, - }, - flagged: { - label: __( 'Failed Moderation', 'hyve-lite' ), - component: FailedModeration, - }, - faq: { - label: __( 'FAQ', 'hyve-lite' ), - component: FAQ, - isPro: true, - }, - }, - }, - messages: { - label: __( 'Messages', 'hyve-lite' ), - icon: comment, - component: Messages, - }, - integrations: { - label: __( 'Integrations', 'hyve-lite' ), - icon: blockMeta, - children: { - integrations: { - label: __( 'Qdrant', 'hyve-lite' ), - component: Qdrant, - }, - search: { - label: __( 'Hyve Connect', 'hyve-lite' ), - component: ExternalSearch, - isPro: ! Boolean( window.hyve.hasPro ), - }, - }, - }, - settings: { - label: __( 'Settings', 'hyve-lite' ), - icon: settings, - children: { - settings: { - label: __( 'General', 'hyve-lite' ), - component: General, - }, - appearance: { - label: __( 'Appearance', 'hyve-lite' ), - component: Appearance, - disabled: true, - isPro: true, - }, - assistant: { - label: __( 'Assistant', 'hyve-lite' ), - component: Assistant, - disabled: true, - }, - moderation: { - label: __( 'Moderation', 'hyve-lite' ), - component: Moderation, - disabled: true, - }, - advanced: { - label: __( 'Advanced', 'hyve-lite' ), - component: Advanced, - disabled: false, - }, - }, - }, -}; - -export const KNOWLEDGE_BASE = { - posts: { - label: __( 'WordPress', 'hyve-lite' ), - description: __( - 'Import your WordPress content into the Knowledge Base.', - 'hyve-lite' - ), - icon: wordpress, - component: Posts, - }, - custom: { - label: __( 'Custom Data', 'hyve-lite' ), - description: __( - 'Add custom data to your Knowledge Base.', - 'hyve-lite' - ), - icon: archive, - component: Custom, - isPro: true, - }, - url: { - label: __( 'Website URL', 'hyve-lite' ), - description: __( - 'Crawl URLs to add content to the Knowledge Base.', - 'hyve-lite' - ), - icon: customLink, - component: URLCrawler, - isPro: true, - }, - sitemap: { - label: __( 'Sitemap', 'hyve-lite' ), - description: __( 'Add a sitemap to the Knowledge Base.', 'hyve-lite' ), - icon: blockMeta, - component: SitemapCrawler, - isPro: true, - }, -}; diff --git a/src/backend/router.js b/src/backend/router.js new file mode 100644 index 00000000..8b44fe67 --- /dev/null +++ b/src/backend/router.js @@ -0,0 +1,367 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { useEffect, useState } from '@wordpress/element'; + +import { applyFilters } from '@wordpress/hooks'; + +import { + archive, + blockMeta, + comment, + customLink, + home, + page, + settings, + wordpress, +} from '@wordpress/icons'; + +/** + * Route registry. The URL is the source of truth + * (`&nav=&sub=&item=`). Sub-panels: `default` is the + * landing panel, `hidden` keeps drill-ins out of the subnav. Pro extends + * entries via the `hyve.routes` filter. + */ +const ROUTES = { + dashboard: { + label: __( 'Dashboard', 'hyve-lite' ), + description: __( + 'An overview of how Hyve is performing on your site.', + 'hyve-lite' + ), + icon: home, + capability: 'manage_options', + requiresAPI: false, + }, + kb: { + label: __( 'Knowledge Base', 'hyve-lite' ), + description: __( + 'The content Hyve draws its answers from.', + 'hyve-lite' + ), + icon: archive, + capability: 'manage_options', + requiresAPI: true, + subs: { + all: { + label: __( 'All sources', 'hyve-lite' ), + default: true, + }, + attention: { + label: __( 'Needs attention', 'hyve-lite' ), + badge: true, + }, + faq: { + label: __( 'FAQ', 'hyve-lite' ), + isPro: true, + }, + 'source-wordpress': { + label: __( 'WordPress', 'hyve-lite' ), + description: __( + 'Import your WordPress content into the Knowledge Base.', + 'hyve-lite' + ), + icon: wordpress, + track: 'posts', + hidden: true, + }, + 'source-custom': { + label: __( 'Custom Data', 'hyve-lite' ), + description: __( + 'Add custom data to your Knowledge Base.', + 'hyve-lite' + ), + icon: archive, + track: 'custom', + hidden: true, + isPro: true, + }, + 'source-url': { + label: __( 'Website URL', 'hyve-lite' ), + description: __( + 'Crawl URLs to add content to the Knowledge Base.', + 'hyve-lite' + ), + icon: customLink, + track: 'url', + hidden: true, + isPro: true, + }, + 'source-sitemap': { + label: __( 'Sitemap', 'hyve-lite' ), + description: __( + 'Add a sitemap to the Knowledge Base.', + 'hyve-lite' + ), + icon: blockMeta, + track: 'sitemap', + hidden: true, + isPro: true, + }, + 'source-documents': { + label: __( 'Documents', 'hyve-lite' ), + description: __( + 'Import PDF, Word, Markdown, Text, or CSV files into the Knowledge Base.', + 'hyve-lite' + ), + icon: page, + track: 'documents', + hidden: true, + isPro: true, + }, + }, + }, + messages: { + label: __( 'Messages', 'hyve-lite' ), + description: __( + 'Every conversation your visitors have had with Hyve.', + 'hyve-lite' + ), + icon: comment, + capability: 'hyve_read_messages', + requiresAPI: true, + subs: { + conversations: { + label: __( 'Conversations', 'hyve-lite' ), + default: true, + }, + thread: { + label: __( 'Conversation', 'hyve-lite' ), + hidden: true, + }, + }, + }, + settings: { + label: __( 'Settings', 'hyve-lite' ), + description: __( + 'Configure the chat, the AI engine, integrations and the plugin.', + 'hyve-lite' + ), + icon: settings, + capability: 'manage_options', + requiresAPI: false, + sidebar: true, + subs: { + 'chat-behavior': { + label: __( 'Behavior', 'hyve-lite' ), + group: __( 'Chat', 'hyve-lite' ), + default: true, + requiresAPI: true, + }, + 'chat-appearance': { + label: __( 'Appearance', 'hyve-lite' ), + group: __( 'Chat', 'hyve-lite' ), + requiresAPI: true, + }, + 'ai-provider': { + label: __( 'Provider & model', 'hyve-lite' ), + group: __( 'AI', 'hyve-lite' ), + }, + 'ai-advanced': { + label: __( 'Advanced', 'hyve-lite' ), + group: __( 'AI', 'hyve-lite' ), + }, + qdrant: { + label: __( 'Qdrant', 'hyve-lite' ), + group: __( 'Integrations', 'hyve-lite' ), + requiresAPI: true, + }, + 'api-access': { + label: __( 'API access', 'hyve-lite' ), + group: __( 'Integrations', 'hyve-lite' ), + requiresAPI: true, + }, + general: { + label: __( 'General', 'hyve-lite' ), + group: __( 'Plugin', 'hyve-lite' ), + requiresAPI: true, + }, + }, + }, +}; + +export const getRoutes = () => applyFilters( 'hyve.routes', ROUTES ); + +/** + * Check whether the current user can access a route. + * + * Route capabilities are also enforced by WordPress REST permissions; this + * keeps unauthorized sections out of the dashboard navigation and prevents + * direct URL access from rendering them. + * + * @param {Object} route Screen entry from the registry. + * + * @return {boolean} Whether the current user can access the route. + */ +const canAccessRoute = ( route ) => { + if ( ! route?.capability ) { + return true; + } + + if ( 'manage_options' === route.capability ) { + return Boolean( window.hyve?.canManage ); + } + + if ( 'hyve_read_messages' === route.capability ) { + return Boolean( window.hyve?.canReadMessages ); + } + + return true; +}; + +/** + * Get routes available to the current user. + * + * @return {Object} The filtered route registry. + */ +export const getAccessibleRoutes = () => + Object.fromEntries( + Object.entries( getRoutes() ).filter( ( [ , route ] ) => + canAccessRoute( route ) + ) + ); + +/** + * Resolve a screen's sub-panel, falling back to its default panel. + * + * @param {Object} route Screen entry from the registry. + * @param {?string} sub Requested sub-panel key. + * + * @return {?string} Resolved sub-panel key. + */ +const resolveSub = ( route, sub ) => { + if ( ! route?.subs ) { + return null; + } + + if ( sub && route.subs[ sub ] ) { + return sub; + } + + const fallback = Object.keys( route.subs ).find( + ( key ) => route.subs[ key ].default + ); + + return fallback || null; +}; + +/** + * Read the current route from the URL, falling back to the dashboard. + * + * @return {{screen: string, sub: ?string, item: ?string}} Current route. + */ +const parseLocation = () => { + const routes = getAccessibleRoutes(); + const params = new URLSearchParams( window.location.search ); + + let screen = params.get( 'nav' ) || window.hyve?.view || 'dashboard'; + const sub = params.get( 'sub' ); + + if ( ! routes[ screen ] ) { + screen = Object.keys( routes )[ 0 ] || 'dashboard'; + } + + return { + screen, + sub: resolveSub( routes[ screen ], sub ), + item: params.get( 'item' ) || null, + }; +}; + +const listeners = new Set(); + +const notify = () => listeners.forEach( ( listener ) => listener() ); + +/** + * Go to a screen (and optionally one of its sub-panels), recording a browser + * history entry. + * + * @param {string} screen Screen key from the registry. + * @param {?string} sub Sub-panel key. + * @param {Object} options Options. + * @param {boolean} options.replace Replace the current history entry. + * @param {?string} options.item Item id for drill-in panels. + */ +export const navigate = ( + screen, + sub = null, + { replace = false, item = null } = {} +) => { + const routes = getAccessibleRoutes(); + + if ( ! routes[ screen ] ) { + return; + } + + const resolved = resolveSub( routes[ screen ], sub ); + const resolvedItem = item ? String( item ) : null; + const current = parseLocation(); + + if ( + current.screen === screen && + current.sub === resolved && + current.item === resolvedItem && + ! replace + ) { + return; + } + + const url = new URL( window.location.href ); + url.searchParams.set( 'nav', screen ); + + if ( resolved ) { + url.searchParams.set( 'sub', resolved ); + } else { + url.searchParams.delete( 'sub' ); + } + + if ( resolvedItem ) { + url.searchParams.set( 'item', resolvedItem ); + } else { + url.searchParams.delete( 'item' ); + } + + window.history[ replace ? 'replaceState' : 'pushState' ]( + { screen, sub: resolved, item: resolvedItem }, + '', + url.toString() + ); + + notify(); + + window.scrollTo( 0, 0 ); + + // Replace navigations are programmatic redirects, not user intent. + if ( ! replace ) { + window.hyveTrk?.add?.( { + feature: 'dashboard', + featureComponent: 'route', + featureValue: resolved ? `${ screen }/${ resolved }` : screen, + } ); + } +}; + +/** + * Current route; re-renders on navigate() and browser back/forward. + * + * @return {{screen: string, sub: ?string, item: ?string}} Current route. + */ +export const useRoute = () => { + const [ route, setRoute ] = useState( parseLocation ); + + useEffect( () => { + const update = () => setRoute( parseLocation() ); + + listeners.add( update ); + window.addEventListener( 'popstate', update ); + + return () => { + listeners.delete( update ); + window.removeEventListener( 'popstate', update ); + }; + }, [] ); + + return route; +}; diff --git a/src/backend/screens/AI.js b/src/backend/screens/AI.js new file mode 100644 index 00000000..9e5c3705 --- /dev/null +++ b/src/backend/screens/AI.js @@ -0,0 +1,372 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { + Button, + ExternalLink, + RangeControl, + SelectControl, + TextControl, +} from '@wordpress/components'; + +import { useDispatch } from '@wordpress/data'; + +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import FieldRow from '../components/FieldRow'; +import useSaveSettings from '../data/useSaveSettings'; + +/** + * Selectable chat models. + * + * Every listed model must support structured outputs (`json_schema`), which + * Hyve requires for chat responses. GPT-3.5 lacks them, so it is excluded. + */ +const MODEL_OPTIONS = [ + { + label: __( 'GPT-5.6 Sol', 'hyve-lite' ), + value: 'gpt-5.6-sol', + description: __( + 'Flagship frontier model, the highest quality at the highest cost.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.6 Terra', 'hyve-lite' ), + value: 'gpt-5.6-terra', + description: __( + 'Newest mini-tier model balancing intelligence and cost.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.6 Luna', 'hyve-lite' ), + value: 'gpt-5.6-luna', + description: __( + 'Newest cost-optimized model, built for high-volume chats.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.5', 'hyve-lite' ), + value: 'gpt-5.5', + description: __( + 'Most capable reasoning model. Thinks before answering, so replies are slower and cost more.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.4', 'hyve-lite' ), + value: 'gpt-5.4', + description: __( + 'Most capable standard model, best for complex questions.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.4 mini', 'hyve-lite' ), + value: 'gpt-5.4-mini', + description: __( + 'Newer model with a strong balance of quality and cost.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5.4 nano', 'hyve-lite' ), + value: 'gpt-5.4-nano', + description: __( + 'Fast and low cost, the recommended default for most chats.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-5', 'hyve-lite' ), + value: 'gpt-5', + description: __( + 'Reasoning model with strong quality at a lower cost than GPT-5.5.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-4.1', 'hyve-lite' ), + value: 'gpt-4.1', + description: __( + 'Capable and proven, great for detailed answers.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-4.1 mini', 'hyve-lite' ), + value: 'gpt-4.1-mini', + description: __( + 'Faster and cheaper than GPT-4.1, a solid all-rounder.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-4.1 nano', 'hyve-lite' ), + value: 'gpt-4.1-nano', + description: __( + 'Ultra-fast and very low cost, best for lightweight chats.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-4o', 'hyve-lite' ), + value: 'gpt-4o', + description: __( + 'Smart, cost-effective general-purpose model.', + 'hyve-lite' + ), + }, + { + label: __( 'GPT-4o mini', 'hyve-lite' ), + value: 'gpt-4o-mini', + description: __( + 'Fastest and most affordable, best for most chats.', + 'hyve-lite' + ), + }, +]; + +const ADVANCED_DEFAULTS = { + similarity_score_threshold: 0.4, +}; + +const getInitialApiStatus = () => { + if ( window.hyve?.isApiKeyConnected ) { + return 'connected'; + } + + return window.hyve?.hasAPIKey ? 'error' : 'none'; +}; + +const ApiStatusChip = ( { status } ) => { + if ( 'connected' === status ) { + return { __( 'Connected', 'hyve-lite' ) }; + } + + if ( 'error' === status ) { + return { __( 'Not connected', 'hyve-lite' ) }; + } + + if ( 'editing' === status ) { + return { __( 'Unsaved', 'hyve-lite' ) }; + } + + return null; +}; + +export const ProviderPanel = () => { + const [ apiStatus, setApiStatus ] = useState( getInitialApiStatus ); + + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting, setHasAPI } = useDispatch( 'hyve' ); + const { createNotice } = useDispatch( 'core/notices' ); + + const onSave = async () => { + const response = await save(); + + if ( ! response ) { + return; + } + + const hasKey = Boolean( settings.api_key ); + + setHasAPI( hasKey ); + + if ( hasKey ) { + window.hyveTrk?.add?.( { + feature: 'openai', + featureComponent: 'api-key', + featureValue: 'added', + } ); + } + + if ( response.warning ) { + createNotice( 'warning', response.warning, { + type: 'snackbar', + isDismissible: true, + } ); + setApiStatus( 'error' ); + } else { + setApiStatus( hasKey ? 'connected' : 'none' ); + } + }; + + // GPT-3.5 is no longer offered (it errors with structured outputs); show + // the default instead. A still-valid but no-longer-listed saved model is + // appended so it isn't silently lost. + const savedModel = settings.chat_model; + const isLegacyModel = + 'string' === typeof savedModel && savedModel.startsWith( 'gpt-3.5' ); + const selectedModel = + ( isLegacyModel ? 'gpt-5.4-nano' : savedModel ) || 'gpt-5.4-nano'; + + const modelOptions = MODEL_OPTIONS.some( + ( option ) => option.value === selectedModel + ) + ? MODEL_OPTIONS + : [ + ...MODEL_OPTIONS, + { + label: selectedModel, + value: selectedModel, + description: __( + 'Your currently selected model.', + 'hyve-lite' + ), + }, + ]; + + const selectedModelOption = modelOptions.find( + ( option ) => option.value === selectedModel + ); + + return ( + + { __( 'Save changes', 'hyve-lite' ) } + + } + > + +
+ { + setSetting( 'api_key', value ); + setApiStatus( 'editing' ); + } } + /> + +
+ { 'none' === apiStatus && ( +

+ + { __( 'Get an API key', 'hyve-lite' ) } + +

+ ) } +
+ + + ( { + label, + value, + } ) ) } + disabled={ isSaving } + onChange={ ( value ) => setSetting( 'chat_model', value ) } + /> + { selectedModelOption?.description && ( +

+ { selectedModelOption.description } +

+ ) } +

+ + { __( 'Compare model pricing', 'hyve-lite' ) } + +

+
+
+ ); +}; + +export const AdvancedPanel = () => { + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const resetDefaults = () => { + Object.entries( ADVANCED_DEFAULTS ).forEach( ( [ key, value ] ) => + setSetting( key, value ) + ); + }; + + return ( + + + + + } + > + + + setSetting( 'similarity_score_threshold', value ) + } + /> + + + ); +}; diff --git a/src/backend/screens/ChatAppearance.js b/src/backend/screens/ChatAppearance.js new file mode 100644 index 00000000..e5f25332 --- /dev/null +++ b/src/backend/screens/ChatAppearance.js @@ -0,0 +1,518 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { + Button, + ColorPicker, + Dropdown, + TextControl, + ToggleControl, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControl as ToggleGroupControl, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControlOption as ToggleGroupControlOption, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, +} from '@wordpress/components'; + +import { MediaUpload } from '@wordpress/media-utils'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { useEffect, useMemo } from '@wordpress/element'; + +import { applyFilters } from '@wordpress/hooks'; + +/** + * Internal dependencies. + */ +import { isLicenseActive, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import FieldRow from '../components/FieldRow'; +import useSaveSettings from '../data/useSaveSettings'; + +const COLOR_OPTIONS = [ + { + label: __( 'Chat background', 'hyve-lite' ), + value: 'chat_background', + }, + { + label: __( 'Assistant background', 'hyve-lite' ), + value: 'assistant_background', + }, + { + label: __( 'User background', 'hyve-lite' ), + value: 'user_background', + }, + { + label: __( 'Icon background', 'hyve-lite' ), + value: 'icon_background', + }, +]; + +const DEFAULT_COLORS = { + chat_background: '#ffffff', + assistant_background: '#ecf1fb', + user_background: '#1155cc', + icon_background: '#1155cc', +}; + +/** + * Mirror of the PHP `is_dark_color()` so the live preview applies the same + * light/dark contrast classes the real frontend does. + * + * @param {string} hex Hex color. + * @return {boolean} True if the color is dark. + */ +const isDarkColor = ( hex ) => { + if ( ! hex ) { + return false; + } + + let color = hex.replace( '#', '' ); + + if ( 3 === color.length ) { + color = color + .split( '' ) + .map( ( c ) => c + c ) + .join( '' ); + } + + if ( 6 !== color.length ) { + return false; + } + + const r = parseInt( color.slice( 0, 2 ), 16 ); + const g = parseInt( color.slice( 2, 4 ), 16 ); + const b = parseInt( color.slice( 4, 6 ), 16 ); + + if ( Number.isNaN( r ) || Number.isNaN( g ) || Number.isNaN( b ) ) { + return false; + } + + return 0.2126 * r + 0.7152 * g + 0.0722 * b < 128; +}; + +const ProChip = () => ( + + { __( 'Pro', 'hyve-lite' ) } + +); + +const ColorTile = ( { label, value, disabled, onChange } ) => { + const tile = ( + <> + + + { label } + + { value.toUpperCase() } + + + + ); + + if ( disabled ) { + return ( + + ); + } + + return ( + ( + + ) } + renderContent={ () => ( + + onChange( + 'string' === typeof newColor + ? newColor + : newColor?.hex + ) + } + /> + ) } + /> + ); +}; + +const ChatAppearance = () => { + const isPro = isLicenseActive(); + + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const chatIconOptions = useMemo( + () => applyFilters( 'hyve.appearance.chat-icons', [] ), + [] + ); + + const chatIcon = useMemo( + () => settings?.chat_icon || {}, + [ settings?.chat_icon ] + ); + + const mediaId = 'media' === chatIcon.type ? Number( chatIcon.value ) : 0; + + const mediaUrl = useSelect( + ( select ) => + mediaId ? select( 'core' ).getMedia( mediaId )?.source_url : null, + [ mediaId ] + ); + + const colors = useMemo( + () => ( { ...DEFAULT_COLORS, ...( settings.colors || {} ) } ), + [ settings.colors ] + ); + + // Push edits into the floating test widget so it previews live, before any + // save. + useEffect( () => { + if ( ! window.hyveApp?.applyPreviewAppearance ) { + return; + } + + const colorsDark = {}; + Object.keys( colors ).forEach( ( key ) => { + colorsDark[ key ] = isDarkColor( colors[ key ] ); + } ); + + const icon = { ...chatIcon }; + if ( 'media' === chatIcon.type && mediaUrl ) { + icon.url = mediaUrl; + } + + window.hyveApp.applyPreviewAppearance( { + chatPosition: 'left' === settings.chat_position ? 'left' : 'right', + showTimestamp: false !== settings.show_timestamp, + chatName: settings.chat_name || '', + colors, + colorsDark, + chatIcon: icon, + } ); + }, [ + settings.chat_position, + settings.show_timestamp, + settings.chat_name, + colors, + chatIcon, + mediaUrl, + ] ); + + const setIcon = ( value ) => { + const nextIcon = { type: 'svg', value }; + + if ( 'default' === value ) { + nextIcon.type = ''; + nextIcon.value = ''; + } + + setSetting( 'chat_icon', nextIcon ); + }; + + return ( + <> + { window.hyveApp?.applyPreviewAppearance && ( +
+
+ + { __( + 'The chat in the corner of this page is your live preview', + 'hyve-lite' + ) } + +

+ { __( + 'Changes below apply to it as you type, before you save.', + 'hyve-lite' + ) } +

+
+
+ ) } + + + + { ! isPro && ( +
+ + { __( + 'A custom name, icon and colors are part of Hyve Pro.', + 'hyve-lite' + ) } + + +
+ ) } + + } + > + + { __( 'Assistant name', 'hyve-lite' ) }{ ' ' } + { ! isPro && } + + } + description={ __( + 'The name shown in the chat header. Leave empty to use the default.', + 'hyve-lite' + ) } + > + + setSetting( 'chat_name', value ) + } + /> + + + + + setSetting( 'chat_position', value ) + } + > + + + + + + + + setSetting( 'show_timestamp', Boolean( value ) ) + } + /> + + + { isPro && 0 < chatIconOptions?.length && ( + + + { chatIconOptions.map( + ( { icon, label, value } ) => { + const CustomIcon = icon; + return ( + } + label={ label } + value={ value } + /> + ); + } + ) } + + + ) } + + + { __( 'Custom icon image', 'hyve-lite' ) }{ ' ' } + { ! isPro && } + + } + description={ __( + 'Use an image from your Media Library for the chat button and header avatar.', + 'hyve-lite' + ) } + > + { mediaUrl && ( + + ) } + + setSetting( 'chat_icon', { + type: 'media', + value: media.id, + } ) + } + render={ ( { open } ) => ( +
+ + { 'media' === chatIcon.type && ( + + ) } +
+ ) } + /> +
+ + + { __( 'Colors', 'hyve-lite' ) }{ ' ' } + { ! isPro && } + + } + description={ __( + 'Chat, assistant, user and launcher backgrounds.', + 'hyve-lite' + ) } + > +
+ { COLOR_OPTIONS.map( ( option ) => ( + + setSetting( 'colors', { + ...settings.colors, + [ option.value ]: color, + } ) + } + /> + ) ) } +
+ { isPro && ( +

+ +

+ ) } +
+
+ + ); +}; + +export default ChatAppearance; diff --git a/src/backend/screens/ChatBehavior.js b/src/backend/screens/ChatBehavior.js new file mode 100644 index 00000000..40ac15c5 --- /dev/null +++ b/src/backend/screens/ChatBehavior.js @@ -0,0 +1,742 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import { + Button, + SelectControl, + TextareaControl, + TextControl, + ToggleControl, +} from '@wordpress/components'; + +import { useDispatch } from '@wordpress/data'; + +import { createInterpolateElement } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import { isLicenseActive, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import FieldRow from '../components/FieldRow'; +import useSaveSettings from '../data/useSaveSettings'; + +const VISIBILITY_OPTIONS = [ + { value: 'all', label: __( 'Show on all pages', 'hyve-lite' ) }, + { value: 'include', label: __( 'Only on selected content', 'hyve-lite' ) }, + { + value: 'exclude', + label: __( 'Everywhere except selected content', 'hyve-lite' ), + }, + { value: 'manual', label: __( "Don't show automatically", 'hyve-lite' ) }, +]; + +const QUESTION_PLACEHOLDERS = [ + __( 'Do you ship to Europe?', 'hyve-lite' ), + __( "What's your refund policy?", 'hyve-lite' ), + __( 'How do I contact support?', 'hyve-lite' ), +]; + +const SaveButton = ( { isSaving, save } ) => ( + +); + +const ProChip = () => ( + + { __( 'Pro', 'hyve-lite' ) } + +); + +const VisibilityCard = () => { + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const displayMode = settings.display_mode ?? 'all'; + const displayRules = Array.isArray( settings.display_rules ) + ? settings.display_rules + : []; + + const updateRule = ( index, key, value ) => + setSetting( + 'display_rules', + displayRules.map( ( rule, i ) => + i === index ? { ...rule, [ key ]: value } : rule + ) + ); + + return ( + } + > +
+
+ { VISIBILITY_OPTIONS.map( ( option ) => ( + + ) ) } +
+ + { ( 'include' === displayMode || + 'exclude' === displayMode ) && ( +
+ { __( 'Content URLs', 'hyve-lite' ) } +

+ { __( + 'Match by URL path. Use “contains” for a whole section (e.g. /shop/), or “matches” for one exact page.', + 'hyve-lite' + ) } +

+ + { displayRules.map( ( rule, index ) => ( +
+ + updateRule( index, 'path', value ) + } + /> + + updateRule( index, 'operator', value ) + } + /> + +
+ ) ) } + + +
+ ) } + +

+ { __( + 'Blocks and shortcodes stay available for inline or manual placement regardless of this setting.', + 'hyve-lite' + ) } +

+
+
+ ); +}; + +const ConversationCard = () => { + const isPro = isLicenseActive(); + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const soundEnabled = Boolean( settings.sound_enabled ?? true ); + + return ( + + + { ! isPro && ( +
+ + { __( + 'Custom instructions are part of Hyve Pro.', + 'hyve-lite' + ) } + + +
+ ) } + + } + > + + + setSetting( 'welcome_message', value ) + } + /> + + + + + setSetting( 'default_message', value ) + } + /> + + + + { __( 'Custom instructions', 'hyve-lite' ) }{ ' ' } + { ! isPro && } + + } + description={ __( + 'Shape the assistant’s tone and persona with your own instructions. The built-in answer format and Knowledge Base rules always stay in effect.', + 'hyve-lite' + ) } + > + + setSetting( 'system_prompt', value ) + } + /> + + + + setSetting( 'sound_enabled', Boolean( value ) ) + } + /> + +
+ ); +}; + +const SuggestionsCard = () => { + const isPro = isLicenseActive(); + + const { settings, isSaving, save } = useSaveSettings(); + + const followUps = Boolean( settings.follow_up_questions ?? true ); + + const { setSetting } = useDispatch( 'hyve' ); + + const questions = Array.isArray( settings.predefined_questions ) + ? settings.predefined_questions + : []; + + const updateQuestion = ( index, value ) => { + const next = [ ...questions ]; + next[ index ] = value; + setSetting( 'predefined_questions', next ); + }; + + return ( + + { __( 'Pro', 'hyve-lite' ) } + + ) + } + footer={ + isPro && + } + > + +
+ { [ 0, 1, 2 ].map( ( index ) => ( + + updateQuestion( index, value ) + } + /> + ) ) } +
+
+ + + + setSetting( 'follow_up_questions', Boolean( value ) ) + } + /> + + + { ! isPro && ( +
+ + { __( + 'Greet visitors with ready-made questions', + 'hyve-lite' + ) } + +

+ { __( + 'Suggested questions and follow-ups are part of Hyve Pro.', + 'hyve-lite' + ) } +

+ +
+ ) } +
+ ); +}; + +const INVITE_PLACEHOLDER = __( + 'Hi there! Have any questions? I can help.', + 'hyve-lite' +); + +const TRIGGER_OPTIONS = [ + { value: 'none', label: __( 'Disabled', 'hyve-lite' ) }, + { value: 'time', label: __( 'Time on page', 'hyve-lite' ) }, + { value: 'exit', label: __( 'Exit intent', 'hyve-lite' ) }, + { value: 'scroll', label: __( 'Scroll depth', 'hyve-lite' ) }, +]; + +const TRIGGER_DESCRIPTIONS = { + none: __( + 'Pick a trigger to show a small bubble next to the closed chat button, inviting the visitor to start a conversation.', + 'hyve-lite' + ), + time: __( + 'The invite appears after the visitor has been on the page for this long.', + 'hyve-lite' + ), + exit: __( + 'The invite appears when the pointer leaves toward the top of the page, as if about to close the tab. Desktop only.', + 'hyve-lite' + ), + scroll: __( + 'The invite appears once the visitor has scrolled this far down the page.', + 'hyve-lite' + ), +}; + +const ProactiveCard = () => { + const isPro = isLicenseActive(); + + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const trigger = settings.proactive_trigger || 'none'; + const enabled = 'none' !== trigger; + + return ( + + { __( 'Pro', 'hyve-lite' ) } + + ) + } + footer={ + isPro && + } + > + +
+ + setSetting( 'proactive_trigger', value ) + } + /> + + { 'time' === trigger && ( + + setSetting( + 'proactive_delay', + Math.max( + 0, + Math.min( + 600, + parseInt( value, 10 ) || 0 + ) + ) + ) + } + /> + ) } + + { 'scroll' === trigger && ( + + setSetting( + 'proactive_scroll_depth', + Math.max( + 1, + Math.min( + 100, + parseInt( value, 10 ) || 1 + ) + ) + ) + } + /> + ) } +
+
+ + { enabled && ( + +
+ + setSetting( 'proactive_message', value ) + } + /> + + { window.hyveApp?.previewTeaser && ( +
+ +
+ ) } +
+
+ ) } + + { ! isPro && ( +
+ + { __( + 'Start conversations before visitors do', + 'hyve-lite' + ) } + +

+ { __( + 'Proactive messages are part of Hyve Pro.', + 'hyve-lite' + ) } +

+ +
+ ) } +
+ ); +}; + +const TrustCard = () => { + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + const sourceLinks = Boolean( settings.show_source_link ); + const privacyNotice = Boolean( settings.privacy_notice_enabled ?? false ); + + const privacySettingsUrl = + window.hyve?.privacySettings || 'options-privacy.php'; + + const privacyLink = ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ); + + return ( + } + > + + + setSetting( 'show_source_link', Boolean( value ) ) + } + /> + + + Settings → Privacy.', + 'hyve-lite' + ), + { a: privacyLink } + ) } + > + + setSetting( 'privacy_notice_enabled', Boolean( value ) ) + } + /> + + { privacyNotice && ! window.hyve?.hasPrivacyPage && ( +
+
+

+ { createInterpolateElement( + __( + 'No Privacy Policy page is set, so the notice won’t appear on your site yet. Choose one under Settings → Privacy.', + 'hyve-lite' + ), + { a: privacyLink } + ) } +

+
+
+ ) } + + + ); +}; + +const ChatBehavior = () => { + return ( + <> + + + + + + + ); +}; + +export default ChatBehavior; diff --git a/src/backend/screens/Dashboard.js b/src/backend/screens/Dashboard.js new file mode 100644 index 00000000..c5a2fc4c --- /dev/null +++ b/src/backend/screens/Dashboard.js @@ -0,0 +1,504 @@ +/** + * WordPress dependencies. + */ +import { __, _n, sprintf } from '@wordpress/i18n'; + +import apiFetch from '@wordpress/api-fetch'; + +import { Button, Icon, SelectControl, Spinner } from '@wordpress/components'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { useEffect, useState } from '@wordpress/element'; + +import { applyFilters } from '@wordpress/hooks'; + +import { archive, brush, cloud, comment, help, people } from '@wordpress/icons'; + +/** + * Internal dependencies. + */ +import { navigate } from '../router'; +import { isLicenseActive, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import SetupChecklist from '../components/SetupChecklist'; +import StatCard from '../components/StatCard'; +import UsageChart from '../components/UsageChart'; + +const VISIBILITY_COPY = { + all: { + title: __( 'Chat is live on all pages', 'hyve-lite' ), + text: __( + 'Visitors can chat with Hyve site-wide. Blocks and shortcodes stay available for manual placement.', + 'hyve-lite' + ), + }, + include: { + title: __( 'Chat is live on selected pages', 'hyve-lite' ), + text: __( + 'Visitors can chat only on the pages matching your URL rules.', + 'hyve-lite' + ), + }, + exclude: { + title: __( 'Chat is live on most pages', 'hyve-lite' ), + text: __( + 'Visitors can chat everywhere except the pages matching your URL rules.', + 'hyve-lite' + ), + }, + manual: { + title: __( 'Chat appears only where you place it', 'hyve-lite' ), + text: __( + 'Hyve shows where you add its block or shortcode, and nowhere else.', + 'hyve-lite' + ), + }, +}; + +const RANGE_OPTIONS = [ + { value: 7, label: __( 'Last 7 days', 'hyve-lite' ) }, + { value: 14, label: __( 'Last 14 days', 'hyve-lite' ) }, + { value: 30, label: __( 'Last 30 days', 'hyve-lite' ) }, + { value: 90, label: __( 'Last 90 days', 'hyve-lite' ) }, +]; + +const GET_STARTED = [ + { + id: 'knowledge-base', + icon: archive, + title: __( 'Grow the Knowledge Base', 'hyve-lite' ), + text: __( + 'Add posts, pages and other sources so Hyve answers more accurately.', + 'hyve-lite' + ), + go: () => navigate( 'kb' ), + }, + { + id: 'settings', + icon: brush, + title: __( 'Personalize the chat', 'hyve-lite' ), + text: __( + 'Set the welcome message, brand the widget, and choose where it appears.', + 'hyve-lite' + ), + go: () => navigate( 'settings', 'chat-behavior' ), + }, + { + id: 'docs', + icon: help, + title: __( 'Need help?', 'hyve-lite' ), + text: __( + 'Browse the docs or reach support whenever you get stuck.', + 'hyve-lite' + ), + go: () => window.open( window.hyve?.docs, '_blank' ), + }, +]; + +const timeAgo = ( dateString ) => { + const seconds = Math.max( + 0, + ( Date.now() - new Date( dateString ).getTime() ) / 1000 + ); + + const formatter = new Intl.RelativeTimeFormat( undefined, { + numeric: 'auto', + style: 'narrow', + } ); + + if ( 3600 > seconds ) { + return formatter.format( + -Math.max( 1, Math.round( seconds / 60 ) ), + 'minute' + ); + } + + if ( 86400 > seconds ) { + return formatter.format( -Math.round( seconds / 3600 ), 'hour' ); + } + + return formatter.format( -Math.round( seconds / 86400 ), 'day' ); +}; + +const VisibilityNotice = ( { mode } ) => { + const copy = VISIBILITY_COPY[ mode ] ?? VISIBILITY_COPY.all; + + return ( +
+
+ + { copy.title } + +

{ copy.text }

+
+ + +
+ ); +}; + +const StatsGrid = () => { + const { stats, totalChunks, isQdrantActive } = useSelect( ( select ) => ( { + stats: select( 'hyve' ).getStats(), + totalChunks: select( 'hyve' ).getTotalChunks(), + isQdrantActive: select( 'hyve' ).isQdrantActive(), + } ) ); + + const sessions = Number( stats.threads ?? 0 ); + const messages = Number( stats.messages ?? 0 ); + const chunks = Number( totalChunks ?? 0 ); + const chunksLimit = Number( window.hyve?.chunksLimit ?? 500 ); + + const usedPercent = Math.min( + 100, + Math.round( ( chunks / chunksLimit ) * 100 ) + ); + + const needsStorage = ! isQdrantActive && 400 < chunks; + + return ( +
+ + + + + + { sprintf( + /* translators: %d: percentage of the free limit used. */ + __( + '%d%% of the free limit used.', + 'hyve-lite' + ), + usedPercent + ) }{ ' ' } + { needsStorage && ( + + ) } + + ) + } + /> + + + { __( 'Coming soon', 'hyve-lite' ) } + + } + foot={ __( + 'Hosted AI usage will show up here once Hyve Connect launches.', + 'hyve-lite' + ) } + /> +
+ ); +}; + +const UsageCard = () => { + const [ range, setRange ] = useState( 30 ); + + const chart = useSelect( ( select ) => select( 'hyve' ).getChart() ); + const hasData = 0 < ( chart?.data?.messages?.length ?? 0 ); + + return ( + { + setRange( Number( value ) ); + window.hyveTrk?.add?.( { + feature: 'charts', + featureComponent: 'days-filter', + featureValue: value, + } ); + } } + /> + ) + } + > + { hasData ? ( + + ) : ( +

+ { __( + 'Usage data will appear here once visitors start chatting.', + 'hyve-lite' + ) } +

+ ) } +
+ ); +}; + +const RecentConversations = () => { + const isPro = isLicenseActive(); + const limit = isPro ? 5 : 3; + + const [ threads, setThreads ] = useState( null ); + const [ hasMore, setHasMore ] = useState( false ); + + useEffect( () => { + const fetchThreads = async () => { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/threads`, + } ); + + setThreads( response.posts ?? [] ); + setHasMore( Boolean( response.more ) ); + } catch { + setThreads( [] ); + } + }; + + fetchThreads(); + }, [] ); + + const rows = ( threads ?? [] ).slice( 0, limit ); + const showUpsell = ! isPro && null !== threads && hasMore; + + return ( + navigate( 'messages' ) }> + { __( 'View all', 'hyve-lite' ) } + + } + > + { null === threads && ( +
+ +
+ ) } + + { threads && 0 === threads.length && ( +

+ { __( + 'Conversations will appear here once visitors start chatting with Hyve.', + 'hyve-lite' + ) } +

+ ) } + + { rows.map( ( thread ) => { + const count = Array.isArray( thread.thread ) + ? thread.thread.length + : 0; + + return ( +
+ + + { thread.title } + + + { sprintf( + /* translators: %d: number of messages in the conversation. */ + _n( + '%d message', + '%d messages', + count, + 'hyve-lite' + ), + count + ) } + + + + + { timeAgo( thread.date ) } + +
+ ); + } ) } + + { showUpsell && ( +
+ + { __( 'Read every conversation', 'hyve-lite' ) } + +

+ { __( + 'Hyve Pro shows the full history, not just the latest three.', + 'hyve-lite' + ) } +

+ +
+ ) } +
+ ); +}; + +const GetStarted = () => { + return ( + <> +

+ { __( 'Get started', 'hyve-lite' ) } +

+ +
+ { GET_STARTED.map( ( item ) => ( + + ) ) } +
+ + ); +}; + +const Dashboard = () => { + const hasAPI = useSelect( ( select ) => select( 'hyve' ).hasAPI() ); + const settings = useSelect( ( select ) => select( 'hyve' ).getSettings() ); + const chunks = useSelect( ( select ) => select( 'hyve' ).getTotalChunks() ); + + const { setStats, setChart, setTotalChunks } = useDispatch( 'hyve' ); + + useEffect( () => { + const fetchStats = async () => { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/stats`, + } ); + + if ( response?.stats ) { + setStats( response.stats ); + setTotalChunks( Number( response.stats.totalChunks ?? 0 ) ); + } + + if ( response?.chart ) { + setChart( response.chart ); + } + } catch { + // Keep the page-load snapshot on failure. + } + }; + + fetchStats(); + }, [ setStats, setChart, setTotalChunks ] ); + + const totalChunks = Number( chunks ?? 0 ); + // Pro keeps the checklist up while its license step is incomplete. + const showChecklist = applyFilters( + 'hyve.setup-required', + ! hasAPI || 0 === totalChunks + ); + + return ( + <> + { showChecklist && } + + { hasAPI && ( + <> + { 0 < totalChunks && ( + + ) } + + + +
+ + +
+ + + + ) } + + ); +}; + +export default Dashboard; diff --git a/src/backend/screens/Integrations.js b/src/backend/screens/Integrations.js new file mode 100644 index 00000000..5e9ddfb9 --- /dev/null +++ b/src/backend/screens/Integrations.js @@ -0,0 +1,414 @@ +/** + * WordPress dependencies. + */ +import { __, sprintf } from '@wordpress/i18n'; + +import apiFetch from '@wordpress/api-fetch'; + +import { + Button, + ExternalLink, + Modal, + TextControl, +} from '@wordpress/components'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import { isLicenseActive, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import FieldRow from '../components/FieldRow'; +import Slot from '../components/Slot'; +import useSaveSettings from '../data/useSaveSettings'; + +export const QdrantPanel = () => { + const { settings, isSaving, save } = useSaveSettings(); + + const isQdrantActive = useSelect( ( select ) => + select( 'hyve' ).isQdrantActive() + ); + + const { setSetting, setQdrantStatus } = useDispatch( 'hyve' ); + const { createNotice } = useDispatch( 'core/notices' ); + + const [ migration, setMigration ] = useState( null ); + const [ isConfirmOpen, setConfirmOpen ] = useState( false ); + const [ isBusy, setBusy ] = useState( false ); + + const timeoutRef = useRef( null ); + + const getStatus = useCallback( async () => { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/qdrant`, + } ); + + if ( response.error ) { + throw new Error( response.error ); + } + + setQdrantStatus( Boolean( response.status ) ); + setMigration( response.migration ?? null ); + + if ( response.migration?.in_progress ) { + timeoutRef.current = setTimeout( getStatus, 10000 ); + } + } catch ( error ) { + createNotice( 'error', error?.message ?? String( error ), { + type: 'snackbar', + isDismissible: true, + } ); + } + }, [ setQdrantStatus, createNotice ] ); + + useEffect( () => { + getStatus(); + + return () => clearTimeout( timeoutRef.current ); + }, [ getStatus ] ); + + const onConnect = async () => { + const response = await save(); + + if ( ! response ) { + return; + } + + await getStatus(); + + window.hyveTrk?.add?.( { + feature: 'qdrant', + featureComponent: 'api-key', + featureValue: 'connected', + } ); + }; + + const onDisconnect = async () => { + setBusy( true ); + + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/qdrant`, + method: 'POST', + } ); + + if ( response.error ) { + throw new Error( response.error ); + } + + setQdrantStatus( false ); + setMigration( null ); + setConfirmOpen( false ); + + createNotice( + 'success', + __( 'Qdrant disconnected.', 'hyve-lite' ), + { + type: 'snackbar', + isDismissible: true, + } + ); + + window.hyveTrk?.add?.( { + feature: 'qdrant', + featureComponent: 'api-key', + featureValue: 'disconnected', + } ); + } catch ( error ) { + createNotice( 'error', error?.message ?? String( error ), { + type: 'snackbar', + isDismissible: true, + } ); + } + + setBusy( false ); + }; + + const isMigrating = isQdrantActive && Boolean( migration?.in_progress ); + + const chip = ( () => { + if ( isMigrating ) { + return { __( 'Migrating', 'hyve-lite' ) }; + } + + if ( isQdrantActive ) { + return { __( 'Connected', 'hyve-lite' ) }; + } + + return { __( 'Not connected', 'hyve-lite' ) }; + } )(); + + const migrated = Number( migration?.current ?? 0 ); + const migrationTotal = Number( migration?.total ?? 0 ); + const migrationPercent = migrationTotal + ? Math.min( 100, Math.round( ( migrated / migrationTotal ) * 100 ) ) + : 0; + + const endpointHost = ( settings.qdrant_endpoint || '' ) + .replace( /^https?:\/\//, '' ) + .replace( /\/$/, '' ); + + return ( + <> + + { isMigrating && ( +
+

+ { __( + 'Moving your Knowledge Base chunks to your cluster. This runs in the background; you can keep working.', + 'hyve-lite' + ) } +

+ +

+ { sprintf( + /* translators: 1: chunks migrated so far, 2: total chunks to migrate. */ + __( '%1$s of %2$s chunks moved.', 'hyve-lite' ), + migrated.toLocaleString(), + migrationTotal.toLocaleString() + ) } +

+
+ ) } + + { ! isMigrating && isQdrantActive && ( + +
+ { endpointHost && ( + + { endpointHost } + + ) } + +
+
+ ) } + + { ! isMigrating && ! isQdrantActive && ( + <> +
+

+ { __( + "Use Qdrant to increase the Knowledge Base limit of Hyve. By integrating Qdrant, you can manage larger datasets and improve query performance for your website. To integrate Qdrant with your application, you'll need an API key and endpoint.", + 'hyve-lite' + ) } +

+

+ { __( + 'Qdrant offers a free plan that supports thousands of data chunks, making it an excellent choice for most use cases without incurring additional costs.', + 'hyve-lite' + ) } +

+

+ + { __( + 'Learn more about Qdrant', + 'hyve-lite' + ) } + +

+
+ +
+ + setSetting( 'qdrant_api_key', value ) + } + /> + + setSetting( 'qdrant_endpoint', value ) + } + /> +
+ +
+
+
+ + ) } +
+ + { isConfirmOpen && ( + setConfirmOpen( false ) } + > +

+ { __( + 'If you proceed, all the data associated with this website will be deleted from Qdrant, and all the posts exceeding the Knowledge Base limit will be removed from the Knowledge Base.', + 'hyve-lite' + ) } +

+
+ + +
+
+ ) } + + ); +}; + +export const ApiAccessPanel = () => { + const isPro = isLicenseActive(); + const hasPro = Boolean( window.hyve?.license ); + + return ( + <> + + { __( 'Pro', 'hyve-lite' ) } + + ) + } + > +
+

+ { __( + 'Enable external services to search your Knowledge Base using advanced semantic search powered by Retrieval-Augmented Generation (RAG) and OpenAI embeddings.', + 'hyve-lite' + ) }{ ' ' } + { __( + 'Integrate automation tools (Zapier, n8n, etc.) with a secure API endpoint to perform intelligent content searches via simple API requests.', + 'hyve-lite' + ) } +

+

+ { __( + 'With an Access Token, you can securely search your content from any location using the API.', + 'hyve-lite' + ) } +

+
+						{ [
+							'curl --request POST',
+							`  --url ${ window.hyve?.rest_url }/knowledge-base/search`,
+							"  --header 'authorization: Bearer hyve_sk_MozAlDXXXXXXXXXXXXXXXX'",
+							"  --header 'content-type: application/json'",
+							"  --data '{",
+							'        "query": "What is the cost of the phone?"',
+							"}'",
+						].join( '\n' ) }
+					
+
+ { ! hasPro && ( +
+ + { __( + 'Search your Knowledge Base from anywhere', + 'hyve-lite' + ) } + +

+ { __( + 'Upgrade to Pro to unlock advanced access management: generate and manage secure API tokens, and control who can access your Knowledge Base via external integrations. Empower your team and automate workflows with confidence and security.', + 'hyve-lite' + ) } +

+ +
+ ) } +
+ + { hasPro && ( + +

+ { __( + 'The access token manager is on its way here.', + 'hyve-lite' + ) } +

+
+ } + /> + ) } + + ); +}; diff --git a/src/backend/screens/KnowledgeBase.js b/src/backend/screens/KnowledgeBase.js new file mode 100644 index 00000000..f36d971c --- /dev/null +++ b/src/backend/screens/KnowledgeBase.js @@ -0,0 +1,1731 @@ +/** + * WordPress dependencies. + */ +import { __, _n, sprintf } from '@wordpress/i18n'; + +import apiFetch from '@wordpress/api-fetch'; + +import { + Button, + Icon, + Modal, + SearchControl, + SelectControl, +} from '@wordpress/components'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { useEffect, useRef, useState } from '@wordpress/element'; + +import { addQueryArgs } from '@wordpress/url'; + +/** + * Internal dependencies. + */ +import { getRoutes, navigate } from '../router'; +import { isLicenseActive, onProcessData, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import ChunkLimitNotice from '../components/ChunkLimitNotice'; +import DataTable from '../components/DataTable'; +import ModerationModal from '../components/ModerationModal'; +import Pagination from '../components/Pagination'; + +/** + * Refresh the pending + moderation count behind the Needs Attention badge. + * + * @param {Function} setAttentionCount Store action. + */ +const fetchAttentionCount = async ( setAttentionCount ) => { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/data/counts`, + } ); + + setAttentionCount( + Number( response.pending ?? 0 ) + Number( response.moderation ?? 0 ) + ); + } catch {} +}; + +const getSources = () => + Object.entries( getRoutes().kb?.subs ?? {} ).filter( ( [ key ] ) => + key.startsWith( 'source-' ) + ); + +const SourcesGrid = () => { + const isPro = isLicenseActive(); + + return ( + +
+
+ { getSources().map( ( [ key, source ] ) => ( + + ) ) } +
+
+
+ ); +}; + +// Pro sources join the Source filter of the unified listing; their values are +// resolved server-side by pro through the `hyve_data_query_args` filter. +const getIndexedSources = () => { + const options = getPostTypes(); + + if ( ! window.hyve?.license ) { + return options; + } + + getSources().forEach( ( [ key, source ] ) => { + if ( 'source-wordpress' === key || ! source.track ) { + return; + } + + options.push( { + label: source.label, + value: `hyve:${ source.track }`, + } ); + } ); + + return options; +}; + +const IndexedContent = () => { + const [ rows, setRows ] = useState( [] ); + const [ hasMore, setHasMore ] = useState( false ); + const [ isLoading, setLoading ] = useState( true ); + const [ isDeleting, setDeleting ] = useState( [] ); + const [ confirmRemove, setConfirmRemove ] = useState( null ); + const [ page, setPage ] = useState( 0 ); + const [ total, setTotal ] = useState( 0 ); + const [ refresh, setRefresh ] = useState( 0 ); + const [ sourceType, setSourceType ] = useState( 'any' ); + const [ search, setSearch ] = useState( '' ); + + // Guards against out-of-order responses when the query changes mid-fetch. + const requestRef = useRef( 0 ); + + // Page size comes from the endpoint. + const perPageRef = useRef( 20 ); + + const totalChunks = useSelect( ( select ) => + select( 'hyve' ).getTotalChunks() + ); + + const { setTotalChunks, setAttentionCount } = useDispatch( 'hyve' ); + const { createNotice } = useDispatch( 'core/notices' ); + + useEffect( () => { + const request = ++requestRef.current; + + setLoading( true ); + + const handler = setTimeout( async () => { + try { + // `hyve:all` asks pro to include its sources in the union; + // plain `any` stays WordPress-only so the old dashboard's + // listings keep their behavior. + const type = + 'any' === sourceType && window.hyve?.license + ? 'hyve:all' + : sourceType; + + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/data`, { + offset: page * perPageRef.current, + status: 'included', + search, + type, + } ), + } ); + + if ( request !== requestRef.current ) { + return; + } + + setRows( response.posts ?? [] ); + setHasMore( Boolean( response.more ) ); + setTotal( Number( response.total ?? 0 ) ); + setTotalChunks( response?.totalChunks ); + + if ( response.per_page ) { + perPageRef.current = Number( response.per_page ); + } + } catch { + if ( request !== requestRef.current ) { + return; + } + + setHasMore( false ); + } + + setLoading( false ); + }, 500 ); + + return () => clearTimeout( handler ); + }, [ page, refresh, search, sourceType, setTotalChunks ] ); + + const onDelete = async ( id ) => { + setDeleting( ( prev ) => [ ...prev, id ] ); + + try { + await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/data`, { id } ), + method: 'DELETE', + } ); + + createNotice( + 'success', + __( 'Post has been removed.', 'hyve-lite' ), + { + type: 'snackbar', + isDismissible: true, + } + ); + + // Refetch so the page refills; step back when the page emptied. + if ( 1 === rows.length && 0 < page ) { + setPage( page - 1 ); + } else { + setRefresh( ( prev ) => prev + 1 ); + } + + // The removed post may have been counted as needing an update. + fetchAttentionCount( setAttentionCount ); + } catch ( error ) { + createNotice( 'error', error?.message ?? String( error ), { + type: 'snackbar', + isDismissible: true, + } ); + } + + setDeleting( ( prev ) => prev.filter( ( postId ) => postId !== id ) ); + }; + + const totalPages = Math.max( 1, Math.ceil( total / perPageRef.current ) ); + + return ( + + ) + } + actions={ + + { sprintf( + /* translators: %s: number of knowledge base chunks. */ + __( '%s chunks', 'hyve-lite' ), + Number( totalChunks ?? 0 ).toLocaleString() + ) } + + } + > +
+

+ { __( + 'All the content that has been added to the Knowledge Base.', + 'hyve-lite' + ) } +

+
+ +
+
+ { + setSourceType( value ); + setPage( 0 ); + } } + /> + { + setSearch( value ); + setPage( 0 ); + } } + /> + { ! isLoading && ( + + { sprintf( + /* translators: %s: number of matching items. */ + _n( + '%s result', + '%s results', + total, + 'hyve-lite' + ), + total.toLocaleString() + ) } + + ) } +
+
+ + ( +
+ + { row.title } + + { row.error && ( + + { sprintf( + // translators: %s: the reason indexing failed, including whether it will be retried. + __( + 'Indexing failed: %s', + 'hyve-lite' + ), + row.error + ) } + + ) } +
+ ), + }, + { + key: 'type', + label: __( 'Source', 'hyve-lite' ), + }, + { + key: 'chunks', + label: __( 'Chunks', 'hyve-lite' ), + align: 'num', + render: ( row ) => + Number( row.chunks ?? 0 ).toLocaleString(), + }, + { + key: 'status', + label: __( 'Status', 'hyve-lite' ), + render: ( row ) => + row.error ? ( + + { __( 'Indexing failed', 'hyve-lite' ) } + + ) : ( + + { __( 'Indexed', 'hyve-lite' ) } + + ), + }, + { + key: 'actions', + label: __( 'Actions', 'hyve-lite' ), + align: 'actions', + render: ( row ) => ( + + ), + }, + ] } + rows={ rows } + isLoading={ isLoading } + empty={ + search + ? __( + 'No indexed content matches your search.', + 'hyve-lite' + ) + : __( + 'Content you add to the Knowledge Base will appear here.', + 'hyve-lite' + ) + } + /> + + { confirmRemove && ( + setConfirmRemove( null ) } + > +

+ { confirmRemove.permanent + ? sprintf( + /* translators: %s: title of the entry being removed. */ + __( + 'Hyve will stop using "%s" in its answers. The entry is removed permanently.', + 'hyve-lite' + ), + confirmRemove.title + ) + : sprintf( + /* translators: %s: title of the content being removed. */ + __( + 'Hyve will stop using "%s" in its answers. The content itself stays on your site, and you can add it back at any time.', + 'hyve-lite' + ), + confirmRemove.title + ) } +

+
+ + +
+
+ ) } +
+ ); +}; + +const excludeTypes = [ 'attachment' ]; + +const getPostTypes = () => { + const postTypes = ( window.hyve?.postTypes ?? [] ).filter( + ( postType ) => ! excludeTypes.includes( postType.value ) + ); + + postTypes.unshift( { label: __( 'All', 'hyve-lite' ), value: 'any' } ); + + return postTypes; +}; + +const VISIBILITY_LABELS = { + public: __( 'Public', 'hyve-lite' ), + private: __( 'Private', 'hyve-lite' ), + password: __( 'Password protected', 'hyve-lite' ), +}; + +const WordPressDrill = () => { + const [ rows, setRows ] = useState( [] ); + const [ hasMore, setHasMore ] = useState( false ); + const [ isLoading, setLoading ] = useState( true ); + const [ isUpdating, setUpdating ] = useState( [] ); + const [ processedPosts, setProcessedPosts ] = useState( [] ); + const [ confirmPost, setConfirmPost ] = useState( null ); + const [ page, setPage ] = useState( 0 ); + const [ total, setTotal ] = useState( 0 ); + const [ query, setQuery ] = useState( { type: 'any', search: '' } ); + + // Selected rows keyed by ID; rows are kept so the queue can run across + // pages and check visibility without refetching. + const [ selected, setSelected ] = useState( {} ); + const [ bulk, setBulk ] = useState( null ); + const [ isBulkConfirmOpen, setBulkConfirmOpen ] = useState( false ); + const [ reviewPost, setReviewPost ] = useState( null ); + + // Guards against out-of-order responses when the query changes mid-fetch. + const requestRef = useRef( 0 ); + + // Page size comes from the endpoint. + const perPageRef = useRef( 20 ); + + const { setTotalChunks, setAttentionCount } = useDispatch( 'hyve' ); + const { createNotice } = useDispatch( 'core/notices' ); + const hasReachedLimit = useSelect( ( select ) => + select( 'hyve' ).hasReachedLimit() + ); + + useEffect( () => { + const request = ++requestRef.current; + + setLoading( true ); + + const handler = setTimeout( async () => { + try { + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/data`, { + offset: page * perPageRef.current, + ...query, + } ), + } ); + + if ( request !== requestRef.current ) { + return; + } + + setRows( response.posts ?? [] ); + setHasMore( Boolean( response.more ) ); + setTotal( Number( response.total ?? 0 ) ); + setTotalChunks( response?.totalChunks ); + + if ( response.per_page ) { + perPageRef.current = Number( response.per_page ); + } + } catch { + if ( request !== requestRef.current ) { + return; + } + + setHasMore( false ); + } + + setLoading( false ); + }, 500 ); + + return () => clearTimeout( handler ); + }, [ query, page, setTotalChunks ] ); + + const onChangeQuery = ( key, value ) => { + setQuery( ( prev ) => ( { ...prev, [ key ]: value } ) ); + setPage( 0 ); + }; + + const totalPages = Math.max( 1, Math.ceil( total / perPageRef.current ) ); + + const processPost = async ( id ) => { + setUpdating( ( prev ) => [ ...prev, id ] ); + + const currentPost = rows.find( ( row ) => row.ID === id ); + + await onProcessData( { + post: currentPost, + onSuccess: () => { + setUpdating( ( prev ) => + prev.filter( ( postId ) => postId !== id ) + ); + setProcessedPosts( ( prev ) => [ ...prev, id ] ); + + window.hyveTrk?.add?.( { + feature: 'knowledge-base', + featureComponent: 'add-data', + featureValue: 'import-wordpress-data', + } ); + }, + onError: ( error ) => { + if ( + 'content_failed_moderation' === error?.code && + undefined !== error.review + ) { + setReviewPost( { + ...currentPost, + review: error.review, + } ); + } + + fetchAttentionCount( setAttentionCount ); + setUpdating( ( prev ) => + prev.filter( ( postId ) => postId !== id ) + ); + }, + } ); + }; + + const onProcess = ( id ) => { + const currentPost = rows.find( ( row ) => row.ID === id ); + + // Restricted content is surfaced to any chat visitor once added, so + // require an explicit confirmation before importing it. + if ( currentPost && 'public' !== currentPost.visibility ) { + setConfirmPost( currentPost ); + return; + } + + processPost( id ); + }; + + const toggleSelected = ( row ) => { + setSelected( ( prev ) => { + const next = { ...prev }; + + if ( next[ row.ID ] ) { + delete next[ row.ID ]; + } else { + next[ row.ID ] = row; + } + + return next; + } ); + }; + + const pageSelectable = rows.filter( + ( row ) => ! processedPosts.includes( row.ID ) + ); + const isPageSelected = + 0 < pageSelectable.length && + pageSelectable.every( ( row ) => selected[ row.ID ] ); + + const togglePage = () => { + setSelected( ( prev ) => { + const next = { ...prev }; + + pageSelectable.forEach( ( row ) => { + if ( isPageSelected ) { + delete next[ row.ID ]; + } else { + next[ row.ID ] = row; + } + } ); + + return next; + } ); + }; + + const runBulk = async ( items ) => { + setBulk( { done: 0, total: items.length } ); + + let added = 0; + + for ( const item of items ) { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/data`, + method: 'POST', + data: { data: item }, + } ); + + if ( response.error ) { + throw new Error( response.error ); + } + + added++; + setProcessedPosts( ( prev ) => [ ...prev, item.ID ] ); + setSelected( ( prev ) => { + const next = { ...prev }; + delete next[ item.ID ]; + return next; + } ); + + window.hyveTrk?.add?.( { + feature: 'knowledge-base', + featureComponent: 'add-data', + featureValue: 'import-wordpress-data', + } ); + } catch { + // Failed items stay selected so they can be retried. + } + + setBulk( ( prev ) => + prev ? { ...prev, done: prev.done + 1 } : prev + ); + } + + setBulk( null ); + fetchAttentionCount( setAttentionCount ); + + const failed = items.length - added; + + if ( failed ) { + createNotice( + 'warning', + sprintf( + /* translators: 1: number of items added, 2: number of items that failed. */ + __( + '%1$s items added, %2$s failed. Failed items stay selected.', + 'hyve-lite' + ), + added, + failed + ), + { type: 'snackbar', isDismissible: true } + ); + return; + } + + createNotice( + 'success', + sprintf( + /* translators: %s: number of items added. */ + _n( + '%s item added to the Knowledge Base.', + '%s items added to the Knowledge Base.', + added, + 'hyve-lite' + ), + added + ), + { type: 'snackbar', isDismissible: true } + ); + }; + + const selectedRows = Object.values( selected ); + const restrictedRows = selectedRows.filter( + ( row ) => 'public' !== row.visibility + ); + + const onBulkAdd = () => { + if ( 0 < restrictedRows.length ) { + setBulkConfirmOpen( true ); + return; + } + + runBulk( selectedRows ); + }; + + return ( + <> +
+ +
+ + + ) + } + > +
+

+ { __( + 'Select posts that are informative, engaging, and relevant. These will be the building blocks that empower your chat assistant to deliver precise and helpful responses. Whether it is answering FAQs or diving into detailed explanations, the content you choose here will shape how effectively your assistant interacts with users.', + 'hyve-lite' + ) } +

+
+ +
+ + + { ( 0 < selectedRows.length || bulk ) && ( +
+ + { sprintf( + /* translators: %s: number of selected items. */ + _n( + '%s item selected', + '%s items selected', + selectedRows.length, + 'hyve-lite' + ), + selectedRows.length + ) } + + + +
+ ) } + +
+ + onChangeQuery( 'type', value ) + } + /> + + onChangeQuery( 'search', value ) + } + /> + { ! isLoading && ( + + { sprintf( + /* translators: %s: number of matching items. */ + _n( + '%s result', + '%s results', + total, + 'hyve-lite' + ), + total.toLocaleString() + ) } + + ) } +
+
+ + ( + + ), + render: ( row ) => + processedPosts.includes( row.ID ) ? null : ( + toggleSelected( row ) } + /> + ), + }, + { + key: 'title', + label: __( 'Title', 'hyve-lite' ), + render: ( row ) => ( +
+ + { row.title } + +
+ ), + }, + { + key: 'type', + label: __( 'Type', 'hyve-lite' ), + }, + { + key: 'visibility', + label: __( 'Visibility', 'hyve-lite' ), + render: ( row ) => + VISIBILITY_LABELS[ row.visibility ] ?? + row.visibility, + }, + { + key: 'actions', + label: __( 'Actions', 'hyve-lite' ), + align: 'actions', + render: ( row ) => + processedPosts.includes( row.ID ) ? ( + + { __( 'Added', 'hyve-lite' ) } + + ) : ( + + ), + }, + ] } + rows={ rows } + isLoading={ isLoading } + /> +
+ + { confirmPost && ( + setConfirmPost( null ) } + > +

+ { 'private' === confirmPost.visibility + ? __( + 'This is a private post. Adding it to the Knowledge Base lets the chatbot surface its content to any visitor, even though the post itself is not publicly viewable.', + 'hyve-lite' + ) + : __( + 'This is a password-protected post. Adding it to the Knowledge Base lets the chatbot surface its content to any visitor without entering the password.', + 'hyve-lite' + ) } +

+
+ + +
+
+ ) } + + { isBulkConfirmOpen && ( + setBulkConfirmOpen( false ) } + > +

+ { sprintf( + /* translators: %s: number of restricted items in the selection. */ + _n( + '%s selected item is private or password protected. Adding it to the Knowledge Base lets the chatbot surface its content to any visitor.', + '%s selected items are private or password protected. Adding them to the Knowledge Base lets the chatbot surface their content to any visitor.', + restrictedRows.length, + 'hyve-lite' + ), + restrictedRows.length + ) } +

+
+ + + +
+
+ ) } + + { reviewPost && ( + setReviewPost( null ) } + onSuccess={ () => { + setProcessedPosts( ( prev ) => [ + ...prev, + reviewPost.ID, + ] ); + setReviewPost( null ); + fetchAttentionCount( setAttentionCount ); + } } + /> + ) } + + ); +}; + +const ISSUE_CHIPS = { + pending: ( + { __( 'Edited since indexing', 'hyve-lite' ) } + ), + moderation: ( + { __( 'Failed moderation', 'hyve-lite' ) } + ), +}; + +const AttentionPanel = () => { + const [ rows, setRows ] = useState( [] ); + const [ isLoading, setLoading ] = useState( true ); + const [ isUpdating, setUpdating ] = useState( [] ); + const [ reviewPost, setReviewPost ] = useState( null ); + const [ bulkUpdate, setBulkUpdate ] = useState( null ); + const [ refresh, setRefresh ] = useState( 0 ); + + const { setTotalChunks, setAttentionCount } = useDispatch( 'hyve' ); + const { createNotice } = useDispatch( 'core/notices' ); + + useEffect( () => { + let isStale = false; + + // Fetches every page of a status; these queues stay small in + // practice, so a merged full list beats a paginated two-source table. + const fetchAll = async ( status ) => { + const collected = []; + let offset = 0; + + for ( let guard = 0; 10 > guard; guard++ ) { + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/data`, { + offset, + status, + } ), + } ); + + collected.push( + ...( response.posts ?? [] ).map( ( post ) => ( { + ...post, + issue: status, + } ) ) + ); + + setTotalChunks( response?.totalChunks ); + + if ( ! response.more ) { + break; + } + + offset = collected.length; + } + + return collected; + }; + + const fetchLists = async () => { + setLoading( true ); + + try { + const [ pending, moderation ] = await Promise.all( [ + fetchAll( 'pending' ), + fetchAll( 'moderation' ), + ] ); + + if ( isStale ) { + return; + } + + setRows( [ ...pending, ...moderation ] ); + setAttentionCount( pending.length + moderation.length ); + } catch {} + + setLoading( false ); + }; + + fetchLists(); + + return () => { + isStale = true; + }; + }, [ refresh, setTotalChunks, setAttentionCount ] ); + + const pendingRows = rows.filter( ( row ) => 'pending' === row.issue ); + + const removeRow = ( id ) => { + setRows( ( prev ) => prev.filter( ( row ) => row.ID !== id ) ); + setAttentionCount( rows.length - 1 ); + }; + + const onUpdate = async ( id ) => { + setUpdating( ( prev ) => [ ...prev, id ] ); + + const currentPost = rows.find( ( row ) => row.ID === id ); + + await onProcessData( { + post: currentPost, + params: { + action: 'update', + }, + onSuccess: () => { + removeRow( id ); + }, + onError: ( error ) => { + if ( + 'content_failed_moderation' === error?.code && + undefined !== error.review + ) { + setReviewPost( { + ...currentPost, + review: error.review, + } ); + } + }, + } ); + + setUpdating( ( prev ) => prev.filter( ( postId ) => postId !== id ) ); + }; + + const onUpdateAll = async () => { + const queue = pendingRows; + + setBulkUpdate( { done: 0, total: queue.length } ); + + let updated = 0; + + for ( const item of queue ) { + try { + const response = await apiFetch( { + path: `${ window.hyve.api }/data`, + method: 'POST', + data: { + action: 'update', + data: item, + }, + } ); + + if ( response.error ) { + throw new Error( response.error ); + } + + updated++; + } catch { + // Failures stay listed; moderation ones move to Review after + // the refresh below. + } + + setBulkUpdate( ( prev ) => + prev ? { ...prev, done: prev.done + 1 } : prev + ); + } + + setBulkUpdate( null ); + setRefresh( ( prev ) => prev + 1 ); + + const failed = queue.length - updated; + + if ( failed ) { + createNotice( + 'warning', + sprintf( + /* translators: 1: number of items updated, 2: number of items that failed. */ + __( '%1$s items updated, %2$s failed.', 'hyve-lite' ), + updated, + failed + ), + { type: 'snackbar', isDismissible: true } + ); + return; + } + + createNotice( + 'success', + sprintf( + /* translators: %s: number of items updated. */ + _n( + '%s item updated.', + '%s items updated.', + updated, + 'hyve-lite' + ), + updated + ), + { type: 'snackbar', isDismissible: true } + ); + }; + + return ( + <> + + { bulkUpdate + ? sprintf( + /* translators: 1: items processed so far, 2: total items in the queue. */ + __( + 'Updating %1$s of %2$s', + 'hyve-lite' + ), + Math.min( + bulkUpdate.done + 1, + bulkUpdate.total + ), + bulkUpdate.total + ) + : __( 'Update all', 'hyve-lite' ) } + + ) + } + > +
+

+ { __( + 'One inbox for content that needs a decision: items edited since they were indexed, and items that failed moderation. Review shows the flagged categories and lets you override a false positive.', + 'hyve-lite' + ) } +

+
+ + ( +
+ + { row.title } + +
+ ), + }, + { + key: 'type', + label: __( 'Source', 'hyve-lite' ), + }, + { + key: 'issue', + label: __( 'Issue', 'hyve-lite' ), + render: ( row ) => ISSUE_CHIPS[ row.issue ], + }, + { + key: 'actions', + label: __( 'Actions', 'hyve-lite' ), + align: 'actions', + render: ( row ) => + 'pending' === row.issue ? ( + + ) : ( +
+ + +
+ ), + }, + ] } + rows={ rows } + isLoading={ isLoading } + empty={ __( + 'Nothing needs your attention right now.', + 'hyve-lite' + ) } + /> +
+ + { reviewPost && ( + setReviewPost( null ) } + onSuccess={ () => { + setReviewPost( null ); + setRefresh( ( prev ) => prev + 1 ); + } } + /> + ) } + + ); +}; + +// Body copy and upsell blocks reuse the old drill-in strings; the pro-owned +// working panels replace these routes via the `hyve.routes` filter. +const LOCKED_COPY = { + 'source-custom': { + body: __( + 'Custom Data allows you to privately feed specific data directly into your chatbot without displaying this information on your public website. With this, you can equip your bot with unique, specialized knowledge that aligns with your business needs and customer queries.', + 'hyve-lite' + ), + title: __( 'Custom Data is a Premium feature', 'hyve-lite' ), + text: __( + 'Privately feed specific data directly into your chatbot, equipping it with specialized knowledge that aligns with your business needs and customer queries. Upgrade now!', + 'hyve-lite' + ), + campaign: 'custom-data-feature', + }, + 'source-url': { + body: __( + 'Crawl URLs to add content to the Knowledge Base.', + 'hyve-lite' + ), + title: __( 'URL Crawling is a Premium feature', 'hyve-lite' ), + text: __( + 'Crawl any web page and add its content to the Knowledge Base. Upgrade now!', + 'hyve-lite' + ), + campaign: 'website-crawling-feature', + }, + 'source-sitemap': { + body: __( + 'Use this tool to crawl a website and add its content to the Knowledge Base using the sitemap.', + 'hyve-lite' + ), + title: __( 'Sitemap Crawling is a Premium feature', 'hyve-lite' ), + text: __( + 'Use this tool to crawl a website and add its content to the Knowledge Base using the sitemap. Upgrade now!', + 'hyve-lite' + ), + campaign: 'sitemap-crawling-feature', + }, + 'source-documents': { + body: __( + 'Import PDF, Word, Markdown, Text, or CSV files from your Media Library into the Knowledge Base.', + 'hyve-lite' + ), + title: __( 'Document Import is a Premium feature', 'hyve-lite' ), + text: __( + 'Upload PDF, Word, Markdown, Text, and CSV files and add their content to the Knowledge Base. Upgrade now!', + 'hyve-lite' + ), + campaign: 'document-import-feature', + }, +}; + +const previewTitleColumn = ( key, label ) => ( { + key, + label, + render: ( row ) => ( +
+ { row[ key ] } +
+ ), +} ); + +const previewActionColumn = ( label, isDestructive = false ) => ( { + key: 'actions', + label: __( 'Actions', 'hyve-lite' ), + align: 'actions', + render: () => ( + + ), +} ); + +// Dummy rows from the old lite locked pages, previewing the pro panels. +const LOCKED_PREVIEWS = { + 'source-custom': { + columns: [ + previewTitleColumn( 'title', __( 'Title', 'hyve-lite' ) ), + previewActionColumn( __( 'Edit', 'hyve-lite' ) ), + ], + rows: [ + __( 'Halloween Limited Time Deal Information', 'hyve-lite' ), + __( 'What to do if my order is missing an item?', 'hyve-lite' ), + __( 'How do I return an item?', 'hyve-lite' ), + __( 'How do I track my order?', 'hyve-lite' ), + __( 'How do I change my delivery address?', 'hyve-lite' ), + __( 'How do I cancel my order?', 'hyve-lite' ), + ].map( ( title, index ) => ( { ID: index + 1, title } ) ), + }, + 'source-url': { + columns: [ + previewTitleColumn( 'title', __( 'URL', 'hyve-lite' ) ), + previewActionColumn( __( 'Delete', 'hyve-lite' ), true ), + ], + rows: Array.from( { length: 5 }, ( _, index ) => ( { + ID: index + 1, + title: `https://example.com/page${ index + 1 }`, + } ) ), + }, + 'source-sitemap': { + columns: [ + previewTitleColumn( 'url', __( 'Sitemap URL', 'hyve-lite' ) ), + { + key: 'status', + label: __( 'Status', 'hyve-lite' ), + render: ( row ) => ( + + { row.done + ? __( 'Completed', 'hyve-lite' ) + : __( 'Queued', 'hyve-lite' ) } + + ), + }, + previewActionColumn( __( 'Details', 'hyve-lite' ) ), + ], + rows: [ false, true, true ].map( ( done, index ) => ( { + ID: index + 1, + url: 'https://example.com/sitemap.xml', + done, + } ) ), + }, + 'source-documents': { + columns: [ + previewTitleColumn( 'title', __( 'Title', 'hyve-lite' ) ), + previewActionColumn( __( 'Delete', 'hyve-lite' ), true ), + ], + rows: Array.from( { length: 5 }, ( _, index ) => ( { + ID: index + 1, + title: `document-${ index + 1 }.pdf`, + } ) ), + }, +}; + +const LockedSource = ( { subKey } ) => { + const isPro = isLicenseActive(); + + const source = getRoutes().kb?.subs?.[ subKey ]; + const copy = LOCKED_COPY[ subKey ]; + + return ( + <> +
+ +
+ + + { __( 'Pro', 'hyve-lite' ) } + + ) + } + > +
+

{ copy?.body ?? source?.description }

+ { isPro && ( +

+ { __( + 'This panel is on its way here.', + 'hyve-lite' + ) } +

+ ) } +
+ + { ! isPro && LOCKED_PREVIEWS[ subKey ] && ( + + ) } + + { ! isPro && copy && ( +
+ { copy.title } +

{ copy.text }

+ +
+ ) } +
+ + ); +}; + +// Dummy rows from the old lite FAQ page, previewing how the pro panel looks. +const FAQ_PREVIEW = [ + { + question: __( 'How do I reset my password?', 'hyve-lite' ), + count: 5, + }, + { + question: __( 'How do I change my email address?', 'hyve-lite' ), + count: 3, + }, + { + question: __( 'How do I update my payment method?', 'hyve-lite' ), + count: 2, + }, + { + question: __( 'How do I cancel my subscription?', 'hyve-lite' ), + count: 1, + }, + { + question: __( 'How do I change my plan?', 'hyve-lite' ), + count: 1, + }, + { + question: __( 'How do I update my billing information?', 'hyve-lite' ), + count: 1, + }, +]; + +// The working FAQ panel is pro-owned (`GET {api}/faq`) and ships with P2. +const FaqPanel = () => { + const isPro = isLicenseActive(); + + return ( + + { __( 'Pro', 'hyve-lite' ) } + + ) + } + > +
+

+ { __( + "The FAQ captures frequently asked questions that went unanswered by our chatbot, providing you with a valuable insight into what your users are seeking. This feature allows you to review these queries and decide whether to incorporate them into your bot's knowledge base. By actively updating your FAQ, you can continuously refine your chatbot's ability to address user needs effectively and enhance their interactive experience. These aren't updated instantly.", + 'hyve-lite' + ) } +

+ { isPro && ( +

+ { __( 'This panel is on its way here.', 'hyve-lite' ) } +

+ ) } +
+ + { ! isPro && ( + <> + + +
+ + { __( 'FAQ is a Premium feature', 'hyve-lite' ) } + +

+ { __( + "Review unanswered questions, enhance your bot's knowledge base, and refine your users' interactive experience. Upgrade now!", + 'hyve-lite' + ) } +

+ +
+ + ) } +
+ ); +}; + +const KnowledgeBase = ( { sub } ) => { + const { setAttentionCount } = useDispatch( 'hyve' ); + + // Keeps the Needs Attention badge current on every panel of the screen. + useEffect( () => { + fetchAttentionCount( setAttentionCount ); + }, [ setAttentionCount ] ); + + // Pro swaps whole panels by attaching `component` to a route sub entry. + const ProPanel = sub ? getRoutes().kb?.subs?.[ sub ]?.component : null; + + if ( ProPanel ) { + return ; + } + + if ( 'source-wordpress' === sub ) { + return ; + } + + if ( sub?.startsWith( 'source-' ) ) { + return ; + } + + if ( 'attention' === sub ) { + return ; + } + + if ( 'faq' === sub ) { + return ; + } + + return ( + <> + + + + ); +}; + +export default KnowledgeBase; diff --git a/src/backend/screens/Messages.js b/src/backend/screens/Messages.js new file mode 100644 index 00000000..28fc800f --- /dev/null +++ b/src/backend/screens/Messages.js @@ -0,0 +1,472 @@ +/** + * WordPress dependencies. + */ +import { __, _n, sprintf } from '@wordpress/i18n'; + +import apiFetch from '@wordpress/api-fetch'; + +import { Button, Modal, Spinner } from '@wordpress/components'; + +import { useDispatch } from '@wordpress/data'; + +import { useEffect, useRef, useState } from '@wordpress/element'; + +import { lock } from '@wordpress/icons'; + +import { addQueryArgs } from '@wordpress/url'; + +/** + * Internal dependencies. + */ +import { navigate } from '../router'; +import { isLicenseActive, setUtm } from '../utils'; +import Card from '../components/Card'; +import Chip from '../components/Chip'; +import Pagination from '../components/Pagination'; + +// Threads already fetched for the list, so the drill-in can open instantly. +const threadCache = new Map(); + +const cacheThreads = ( posts ) => + posts.forEach( ( post ) => threadCache.set( String( post.ID ), post ) ); + +const shortDate = ( dateString ) => { + const date = new Date( dateString ); + + const options = { month: 'short', day: 'numeric' }; + + if ( date.getFullYear() !== new Date().getFullYear() ) { + options.year = 'numeric'; + } + + return new Intl.DateTimeFormat( undefined, options ).format( date ); +}; + +const timeOfDay = ( unixSeconds ) => + new Intl.DateTimeFormat( undefined, { + hour: 'numeric', + minute: '2-digit', + } ).format( new Date( unixSeconds * 1000 ) ); + +const messageCount = ( thread ) => + Array.isArray( thread.thread ) ? thread.thread.length : 0; + +const snippet = ( thread ) => { + const last = Array.isArray( thread.thread ) + ? thread.thread[ thread.thread.length - 1 ] + : null; + + return ( last?.message || '' ).replace( /<[^>]+>/g, '' ).trim(); +}; + +const ExportAction = () => { + if ( ! window.hyve?.canManageMessages ) { + return null; + } + + const exportURL = window.hyve?.exportMessagesURL; + + if ( exportURL ) { + return ( + + ); + } + + return ( + <> + + { __( 'Pro', 'hyve-lite' ) } + + + + ); +}; + +const ConversationsPanel = () => { + const isPro = isLicenseActive(); + + const [ posts, setPosts ] = useState( [] ); + const [ hasMore, setHasMore ] = useState( false ); + const [ isLoading, setLoading ] = useState( true ); + const [ page, setPage ] = useState( 0 ); + const [ total, setTotal ] = useState( 0 ); + + // Page size comes from the endpoint (3 on free, 10 on pro). + const perPageRef = useRef( 0 ); + + useEffect( () => { + const fetchThreads = async () => { + setLoading( true ); + + try { + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/threads`, { + offset: page * perPageRef.current, + } ), + } ); + + const threads = response.posts ?? []; + + cacheThreads( threads ); + setPosts( threads ); + setHasMore( Boolean( response.more ) ); + setTotal( Number( response.total ?? 0 ) ); + + if ( response.per_page ) { + perPageRef.current = Number( response.per_page ); + } + } catch { + setHasMore( false ); + } + + setLoading( false ); + }; + + fetchThreads(); + }, [ page ] ); + + const totalPages = perPageRef.current + ? Math.max( 1, Math.ceil( total / perPageRef.current ) ) + : 1; + + return ( + } + footer={ + isPro && + ( 0 < page || hasMore ) && ( + + ) + } + > + { isLoading && 0 === posts.length && ( +
+ +
+ ) } + + { ! isLoading && 0 === posts.length && ( +

+ { __( + 'Conversations will appear here once visitors start chatting with Hyve.', + 'hyve-lite' + ) } +

+ ) } + + { 0 < posts.length && ( +
+ + + + + + + + + + + { posts.map( ( post ) => ( + + + + + + + ) ) } + +
{ __( 'Conversation', 'hyve-lite' ) }{ __( 'Messages', 'hyve-lite' ) }{ __( 'Date', 'hyve-lite' ) } + + { __( 'Actions', 'hyve-lite' ) } + +
+ + { post.title } + + { snippet( post ) && ( + + { snippet( post ) } + + ) } + + { messageCount( post ) } + { shortDate( post.date ) } + +
+
+ ) } + + { ! isPro && hasMore && ( +
+ + { __( 'Read every conversation', 'hyve-lite' ) } + +

+ { __( + 'Hyve Pro shows the full history, not just the latest three.', + 'hyve-lite' + ) } +

+ +
+ ) } +
+ ); +}; + +const ThreadView = ( { item } ) => { + const [ thread, setThread ] = useState( + () => threadCache.get( String( item ) ) ?? null + ); + const [ isDeleting, setDeleting ] = useState( false ); + const [ isConfirmOpen, setConfirmOpen ] = useState( false ); + + const { createNotice } = useDispatch( 'core/notices' ); + + useEffect( () => { + if ( thread ) { + return; + } + + // Deep link: the list was never loaded, so look the thread up in the + // first page of results. + const findThread = async () => { + try { + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/threads`, { + offset: 0, + } ), + } ); + + cacheThreads( response.posts ?? [] ); + setThread( threadCache.get( String( item ) ) ?? false ); + } catch { + setThread( false ); + } + }; + + findThread(); + }, [ thread, item ] ); + + const onDelete = async () => { + if ( ! window.hyve?.canManageMessages ) { + return; + } + + setDeleting( true ); + + try { + const response = await apiFetch( { + path: addQueryArgs( `${ window.hyve.api }/threads`, { + id: thread.ID, + } ), + method: 'DELETE', + } ); + + if ( response.data ) { + threadCache.delete( String( thread.ID ) ); + createNotice( 'success', response.data, { + type: 'snackbar', + isDismissible: true, + } ); + + window.hyveTrk?.add?.( { + feature: 'dashboard', + featureComponent: 'messages-tab', + featureValue: 'delete-thread', + } ); + + navigate( 'messages', 'conversations' ); + return; + } + } catch ( error ) { + createNotice( 'error', error?.message, { + type: 'snackbar', + isDismissible: true, + } ); + } + + setDeleting( false ); + }; + + return ( + <> +
+ +
+ + { null === thread && ( +
+ +
+ ) } + + { false === thread && ( +
+

{ __( 'Conversation not found', 'hyve-lite' ) }

+

+ { __( + 'It may have been deleted, or the link is out of date.', + 'hyve-lite' + ) } +

+
+ ) } + + { thread && ( + setConfirmOpen( true ) } + > + { __( 'Delete conversation', 'hyve-lite' ) } + + ) + } + > +
+ { shortDate( thread.date ) } + { ' · ' } + { sprintf( + /* translators: %d: number of messages in the conversation. */ + _n( + '%d message', + '%d messages', + messageCount( thread ), + 'hyve-lite' + ), + messageCount( thread ) + ) } + { thread.thread_id && ( + <> + { ' · ' } + { thread.thread_id.replace( 'thread_', '' ) } + + ) } +
+ +
+ { ( thread.thread ?? [] ).map( ( message, index ) => { + if ( 'bot' === message.sender ) { + return ( +
+

+ +

+ ); + } + + if ( 'user' === message.sender ) { + return ( +
+

{ message.message }

+ +
+ ); + } + + return null; + } ) } +
+
+ ) } + + { isConfirmOpen && thread && ( + setConfirmOpen( false ) } + > +

+ { __( + 'It will be removed permanently. This cannot be undone.', + 'hyve-lite' + ) } +

+
+ + +
+
+ ) } + + ); +}; + +const Messages = ( { sub, item } ) => { + if ( 'thread' === sub && item ) { + return ; + } + + return ; +}; + +export default Messages; diff --git a/src/backend/screens/Settings.js b/src/backend/screens/Settings.js new file mode 100644 index 00000000..10ae220d --- /dev/null +++ b/src/backend/screens/Settings.js @@ -0,0 +1,55 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies. + */ +import { getRoutes } from '../router'; +import ChatBehavior from './ChatBehavior'; +import ChatAppearance from './ChatAppearance'; +import { ProviderPanel, AdvancedPanel } from './AI'; +import SettingsGeneral from './SettingsGeneral'; +import { QdrantPanel, ApiAccessPanel } from './Integrations'; + +const PANELS = { + 'chat-behavior': ChatBehavior, + 'chat-appearance': ChatAppearance, + 'ai-provider': ProviderPanel, + 'ai-advanced': AdvancedPanel, + qdrant: QdrantPanel, + 'api-access': ApiAccessPanel, + general: SettingsGeneral, +}; + +const Settings = ( { sub } ) => { + const hasLoaded = useSelect( ( select ) => select( 'hyve' ).hasLoaded() ); + + if ( ! hasLoaded ) { + return null; + } + + // Pro swaps whole panels by attaching `component` to a route sub entry. + const Panel = + getRoutes().settings?.subs?.[ sub ]?.component ?? PANELS[ sub ]; + + if ( Panel ) { + return ; + } + + return ( +
+

+ { __( + 'This panel is on its way. Use the sidebar to move around; the URL updates so every view is linkable.', + 'hyve-lite' + ) } +

+
+ ); +}; + +export default Settings; diff --git a/src/backend/screens/SettingsGeneral.js b/src/backend/screens/SettingsGeneral.js new file mode 100644 index 00000000..351b0b2a --- /dev/null +++ b/src/backend/screens/SettingsGeneral.js @@ -0,0 +1,94 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +import { ToggleControl } from '@wordpress/components'; + +import { useDispatch } from '@wordpress/data'; + +import { useEffect, useState } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import Card from '../components/Card'; +import FieldRow from '../components/FieldRow'; +import useSaveSettings from '../data/useSaveSettings'; + +const SettingsGeneral = () => { + const { settings, isSaving, save } = useSaveSettings(); + + const { setSetting } = useDispatch( 'hyve' ); + + // Toggles auto-save; the effect runs after the store update so save() posts + // the fresh value. + const [ isDirty, setDirty ] = useState( false ); + + useEffect( () => { + if ( ! isDirty ) { + return; + } + + setDirty( false ); + save(); + }, [ isDirty, save ] ); + + const toggle = ( key, value ) => { + setSetting( key, Boolean( value ) ); + setDirty( true ); + }; + + const rowAction = Boolean( settings.post_row_addon_enabled ); + const telemetry = Boolean( settings.telemetry_enabled ); + + return ( + + + + toggle( 'post_row_addon_enabled', value ) + } + /> + + + + + toggle( 'telemetry_enabled', value ) + } + /> + + + ); +}; + +export default SettingsGeneral; diff --git a/src/backend/store.js b/src/backend/store.js index 5486b8d2..015049ef 100644 --- a/src/backend/store.js +++ b/src/backend/store.js @@ -4,13 +4,17 @@ import { createReduxStore, register } from '@wordpress/data'; const DEFAULT_STATE = { - route: 'home', + route: window.hyve?.view || 'dashboard', hasLoaded: false, settings: {}, processed: [], hasAPI: Boolean( window.hyve.hasAPIKey ), isQdrantActive: Boolean( window.hyve.isQdrantActive ), - totalChunks: 0, + stats: window.hyve.stats || {}, + chart: window.hyve.chart || null, + totalChunks: Number( window.hyve.stats?.totalChunks ?? 0 ), + attentionCount: null, + serviceErrors: window.hyve.serviceErrors || [], }; const actions = { @@ -50,12 +54,36 @@ const actions = { totalChunks, }; }, + setStats( stats ) { + return { + type: 'SET_STATS', + stats, + }; + }, + setChart( chart ) { + return { + type: 'SET_CHART', + chart, + }; + }, setQdrantStatus( isQdrantActive ) { return { type: 'SET_QDRANT_STATUS', isQdrantActive, }; }, + setAttentionCount( attentionCount ) { + return { + type: 'SET_ATTENTION_COUNT', + attentionCount, + }; + }, + setServiceErrors( serviceErrors ) { + return { + type: 'SET_SERVICE_ERRORS', + serviceErrors, + }; + }, }; const selectors = { @@ -74,15 +102,27 @@ const selectors = { getTotalChunks( state ) { return state.totalChunks; }, + getStats( state ) { + return state.stats; + }, + getChart( state ) { + return state.chart; + }, hasReachedLimit( state ) { return ( window.hyve.chunksLimit <= Number( state.totalChunks ) && - ! Boolean( window.hyve.isQdrantActive ) + ! state.isQdrantActive ); }, isQdrantActive( state ) { return state.isQdrantActive; }, + getAttentionCount( state ) { + return state.attentionCount; + }, + getServiceErrors( state ) { + return state.serviceErrors; + }, }; const reducer = ( state = DEFAULT_STATE, action ) => { @@ -120,11 +160,31 @@ const reducer = ( state = DEFAULT_STATE, action ) => { ...state, totalChunks: action.totalChunks, }; + case 'SET_STATS': + return { + ...state, + stats: action.stats, + }; + case 'SET_CHART': + return { + ...state, + chart: action.chart, + }; case 'SET_QDRANT_STATUS': return { ...state, isQdrantActive: action.isQdrantActive, }; + case 'SET_ATTENTION_COUNT': + return { + ...state, + attentionCount: action.attentionCount, + }; + case 'SET_SERVICE_ERRORS': + return { + ...state, + serviceErrors: action.serviceErrors, + }; default: return state; } diff --git a/src/backend/style.scss b/src/backend/style.scss index 8f34c468..4aa65ca2 100644 --- a/src/backend/style.scss +++ b/src/backend/style.scss @@ -1,97 +1,1817 @@ /** -* Note: Do not use only `@import "tailwindcss";` since it is using cascade layers -* which have a lower priority than normal CSS in WordPress React Component -*/ -@import "tailwindcss/theme.css"; -@import "tailwindcss/preflight.css"; -@import "tailwindcss/utilities.css"; + * New dashboard styles, scoped under .hyve-next so they never leak into the + * current UI. Palette values mirror ui/mockup-v2.html. + */ -#wpcontent { - padding: 0; +/* + * Tokens, wp-components accent overrides (WP admin blue instead of the + * Gutenberg indigo) and compact control sizing. Modals portal to , + * so they get all of it via the .hyve-next-modal class. + */ +.hyve-next, +.hyve-next-modal { + --hyve-wp-blue: #2271b1; + --hyve-wp-blue-h: #135e96; + --hyve-brand-a: #4d8af5; + --hyve-brand-b: #15c; + --hyve-text: #1e1e1e; + --hyve-text-2: #3c434a; + --hyve-muted: #757575; + --hyve-faint: #a7aaad; + --hyve-border: #dcdcde; + --hyve-border-2: #c3c4c7; + --hyve-bg: #f0f0f1; + --hyve-surface: #fff; + --hyve-surface-2: #f6f7f7; + --hyve-ok: #007017; + --hyve-ok-bg: #edfaef; + --hyve-ok-dot: #00a32a; + --hyve-warn: #8a6d00; + --hyve-warn-bg: #fcf9e8; + --hyve-warn-dot: #dba617; + --hyve-bad: #b32d2e; + --hyve-bad-bg: #fcf0f1; + --hyve-bad-dot: #d63638; + --hyve-radius: 2px; + --hyve-radius-in: 4px; + --hyve-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + --wp-admin-theme-color: #2271b1; + --wp-admin-theme-color-darker-10: #135e96; + --wp-admin-theme-color-darker-20: #0a4b78; + --wp-components-color-accent: #2271b1; + --wp-components-color-accent-darker-10: #135e96; + --wp-components-color-accent-darker-20: #0a4b78; + + .components-button:not(.is-link) { + font-size: 12.5px; + height: 30px; + } + + .components-text-control__input, + .components-select-control__input, + .components-input-control__input, + .components-search-control__input { + font-size: 13px; + height: 30px; + min-height: 30px; + + &:disabled { + background: var(--hyve-surface-2); + color: var(--hyve-muted); + cursor: default; + opacity: 0.7; + } + } +} + +.hyve-next { + min-height: 100vh; + background: var(--hyve-bg); + color: var(--hyve-text-2); + font-size: 13px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + + /* Zero-specificity reset via :where() so any single class beats it. */ + :where(h1, h2, h3, p) { + margin: 0; + } +} + +.hyve-next__bar { + align-items: center; + background: var(--hyve-surface); + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 14px; + height: 64px; + padding: 0 24px; + position: sticky; + top: var(--wp-admin--admin-bar--height, 32px); + z-index: 8; + + @media (max-width: 782px) { + gap: 10px; + padding: 0 16px; + } + + /* Below 600px the admin bar scrolls away, so pin to the viewport. */ + @media (max-width: 600px) { + top: 0; + } +} + +.hyve-next__mark { + display: block; + flex: 0 0 auto; + height: 30px; + width: 30px; +} + +.hyve-next__name { + color: var(--hyve-text); + font-size: 16px; + font-weight: 700; + letter-spacing: -0.01em; +} + +.hyve-next__pill { + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border); + border-radius: 20px; + color: var(--hyve-muted); + font-size: 11px; + font-weight: 600; + padding: 1px 7px; + + @media (max-width: 640px) { + display: none; + } +} + +.hyve-next__spacer { + flex: 1; +} + +.hyve-next__status { + align-items: center; + color: var(--hyve-text-2); + display: inline-flex; + font-size: 12px; + font-weight: 600; + gap: 7px; + white-space: nowrap; +} + +/* On phones the dot carries the status visually; the label stays readable + for screen readers and surfaces as a tooltip via the title attribute. */ +@media (max-width: 640px) { + + .hyve-next__status-label { + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + width: 1px; + } +} + +.hyve-next__status-dot { + background: var(--hyve-ok-dot); + border-radius: 50%; + box-shadow: 0 0 0 3px var(--hyve-ok-bg); + height: 8px; + width: 8px; + + .is-setup & { + background: var(--hyve-warn-dot); + box-shadow: 0 0 0 3px var(--hyve-warn-bg); + } + +} + +/* License plan pill, sits with the name and version. */ +.hyve-next__plan { + background: var(--hyve-ok-bg); + border: 1px solid #b8e6c1; + border-radius: 20px; + color: var(--hyve-ok); + cursor: pointer; + font-family: inherit; + font-size: 11px; + font-weight: 600; + line-height: 1.5; + padding: 1px 8px; + white-space: nowrap; + + &.is-warn { + background: var(--hyve-warn-bg); + border-color: #ecd9a2; + color: var(--hyve-warn); + } + + &.is-bad { + background: var(--hyve-bad-bg); + border-color: #f2c8c9; + color: var(--hyve-bad); + } + + &:hover { + border-color: currentcolor; + } +} + +.hyve-next__doclink { + align-items: center; + color: var(--hyve-wp-blue); + display: inline-flex; + font-size: 13px; + font-weight: 500; + gap: 4px; + text-decoration: none; + + @media (max-width: 640px) { + display: none; + } + + svg { + fill: currentcolor; + } + + &:hover { + color: var(--hyve-wp-blue-h); + } +} + +.hyve-next__cta { + align-items: center; + background: var(--hyve-wp-blue); + border: 1px solid var(--hyve-wp-blue); + border-radius: var(--hyve-radius); + color: #fff; + display: inline-flex; + font-size: 12px; + font-weight: 500; + height: 28px; + padding: 0 9px; + text-decoration: none; + white-space: nowrap; + + &:hover, + &:focus { + background: var(--hyve-wp-blue-h); + color: #fff; + } +} + +.hyve-next__tabs { + background: var(--hyve-surface); + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 4px; + overflow-x: auto; + padding: 0 24px; + position: sticky; + top: calc(var(--wp-admin--admin-bar--height, 32px) + 64px); + z-index: 7; + + @media (max-width: 782px) { + padding: 0 16px; + } + + @media (max-width: 600px) { + top: 64px; + } +} + +.hyve-next__tab { + align-items: center; + background: transparent; + border: 0; + border-bottom: 2px solid transparent; + color: var(--hyve-text-2); + cursor: pointer; + display: inline-flex; + font-size: 13px; + font-weight: 500; + gap: 7px; + padding: 14px 12px; + white-space: nowrap; + + svg { + fill: currentcolor; + opacity: 0.7; + } + + &:hover { + color: var(--hyve-wp-blue); + } + + &.is-active { + border-bottom-color: var(--hyve-wp-blue); + color: var(--hyve-wp-blue); + font-weight: 600; + + svg { + opacity: 1; + } + } + + &:disabled { + color: var(--hyve-text-2); + cursor: default; + opacity: 0.35; + } +} + +.hyve-next__subnav { + display: flex; + flex-wrap: wrap; + font-size: 13px; + margin-bottom: 18px; + row-gap: 6px; +} + +.hyve-next__sublink { + background: transparent; + border: 0; + border-right: 1px solid var(--hyve-border-2); + color: var(--hyve-wp-blue); + cursor: pointer; + line-height: 1.4; + padding: 0 10px; + + &:first-child { + padding-left: 0; + } + + &:last-child { + border-right: 0; + } + + &:hover { + color: var(--hyve-wp-blue-h); + } + + &.is-active { + color: var(--hyve-text); + font-weight: 600; + } +} + +.hyve-next__wrap { + margin: 0 auto; + max-width: 1100px; + padding: 24px 24px 56px; + + @media (max-width: 782px) { + padding: 16px 16px 48px; + } +} + +.hyve-next-settings { + align-items: start; + display: grid; + gap: 22px; + grid-template-columns: 210px minmax(0, 1fr); + + @media (max-width: 980px) { + grid-template-columns: minmax(0, 1fr); + } +} + +.hyve-next-side { + display: flex; + flex-direction: column; + gap: 1px; + position: sticky; + top: calc(var(--wp-admin--admin-bar--height, 32px) + 136px); + + @media (max-width: 980px) { + flex-direction: row; + flex-wrap: wrap; + position: static; + } +} + +.hyve-next-side__group { + color: var(--hyve-muted); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.05em; + margin: 16px 0 4px; + padding: 0 12px; + text-transform: uppercase; + + &:first-child { + margin-top: 0; + } + + @media (max-width: 980px) { + width: 100%; + } +} + +.hyve-next-side__link { + background: transparent; + border: 0; + border-left: 3px solid transparent; + border-radius: 0 2px 2px 0; + color: var(--hyve-text-2); + cursor: pointer; + font-size: 13px; + padding: 7px 12px; + text-align: left; + + &:disabled { + color: var(--hyve-text-2); + cursor: default; + opacity: 0.35; + } + + &:hover:not(:disabled) { + color: var(--hyve-wp-blue); + } + + &.is-active { + background: #f0f6fc; + border-left-color: var(--hyve-wp-blue); + color: var(--hyve-wp-blue); + font-weight: 600; + } +} + +.hyve-next-card { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + margin-bottom: 20px; +} + +.hyve-next-card__head { + align-items: center; + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 10px; + padding: 14px 16px; + + h2 { + color: var(--hyve-text); + flex: 1; + font-size: 14px; + font-weight: 600; + } + + @media (max-width: 640px) { + flex-wrap: wrap; + } +} + +.hyve-next-card__actions { + align-items: center; + display: flex; + gap: 8px; +} + +.hyve-next-card__foot { + align-items: center; + background: var(--hyve-surface-2); + border-top: 1px solid var(--hyve-border); + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 14px 16px; +} + +.hyve-next-card__foot-upsell { + align-items: center; + color: var(--hyve-muted); + display: flex; + flex-wrap: wrap; + font-size: 12.5px; + gap: 10px; + margin-left: auto; + text-align: right; +} + +.hyve-next-field { + align-items: start; + border-bottom: 1px solid var(--hyve-border); + display: grid; + gap: 24px; + grid-template-columns: 250px minmax(0, 1fr); + padding: 16px; + + &:last-child { + border-bottom: 0; + } + + @media (max-width: 980px) { + grid-template-columns: minmax(0, 1fr); + gap: 8px; + } +} + +.hyve-next-field__label { + color: var(--hyve-text); + font-size: 13px; + font-weight: 600; +} + +.hyve-next-field__info p { + color: var(--hyve-muted); + font-size: 12.5px; + line-height: 1.5; + margin-top: 3px; +} + +.hyve-next-field__control { + max-width: 420px; + + &.is-wide { + max-width: none; + } +} + +.hyve-next-field__inline { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 10px; + + > :first-child { + flex: 0 0 auto; + max-width: 100%; + width: 420px; + } +} + +.hyve-next-field__hint { + font-size: 12px; + margin-top: 6px; +} + +.hyve-next-chip { + align-items: center; + border-radius: var(--hyve-radius); + display: inline-flex; + font-size: 11.5px; + font-weight: 600; + gap: 6px; + padding: 2px 9px; + white-space: nowrap; + + &.is-ok { + background: var(--hyve-ok-bg); + color: var(--hyve-ok); + } + + &.is-warn { + background: var(--hyve-warn-bg); + color: var(--hyve-warn); + } + + &.is-bad { + background: var(--hyve-bad-bg); + color: var(--hyve-bad); + } + + &.is-muted { + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border); + color: var(--hyve-muted); + } + + &.is-planned { + background: var(--hyve-surface-2); + border: 1px dashed var(--hyve-border-2); + color: var(--hyve-muted); + font-size: 9.5px; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; + } + + &.is-pro { + background: #f0f6fc; + border: 1px solid #c5d9ec; + color: var(--hyve-wp-blue); + font-size: 9.5px; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; + } +} + +.hyve-next-chip__dot { + border-radius: 50%; + height: 6px; + width: 6px; + + .is-ok > & { + background: var(--hyve-ok-dot); + } + + .is-warn > & { + background: var(--hyve-warn-dot); + } + + .is-bad > & { + background: var(--hyve-bad-dot); + } + + .is-muted > & { + background: var(--hyve-faint); + } +} + +.hyve-next-checklist { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + margin-bottom: 20px; +} + +.hyve-next-checklist__head { + align-items: center; + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 10px; + justify-content: space-between; + padding: 14px 16px; + + h2 { + color: var(--hyve-text); + font-size: 14px; + font-weight: 600; + } +} + +.hyve-next-checklist__progress { + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + color: var(--hyve-muted); + font-size: 11.5px; + font-weight: 600; + padding: 2px 9px; + white-space: nowrap; +} + +.hyve-next-checklist__step { + align-items: center; + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 13px; + padding: 13px 16px; + + &.is-locked { + opacity: 0.55; + } + + @media (max-width: 640px) { + flex-wrap: wrap; + } +} + +.hyve-next-checklist__num { + align-items: center; + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border-2); + border-radius: 50%; + color: var(--hyve-text-2); + display: flex; + flex: 0 0 auto; + font-size: 12px; + font-weight: 700; + height: 26px; + justify-content: center; + width: 26px; + + /* stylelint-disable-next-line no-descending-specificity */ + svg { + fill: currentcolor; + } + + .is-done & { + background: var(--hyve-ok-bg); + border-color: var(--hyve-ok-dot); + color: var(--hyve-ok); + } +} + +.hyve-next-checklist__body { + flex: 1; + + /* A floor on the text is what lets the action wrap below it. */ + @media (max-width: 640px) { + min-width: 200px; + } + + b { + color: var(--hyve-text); + display: block; + font-size: 13px; + } + + p { + color: var(--hyve-muted); + font-size: 12px; + margin-top: 1px; + } +} + +.hyve-next-checklist__optional { + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + color: var(--hyve-muted); + font-size: 11px; + font-weight: 600; + margin-left: 6px; + padding: 1px 7px; + vertical-align: 1px; +} + +.hyve-next-checklist__foot { + border-top: 1px solid var(--hyve-border); + color: var(--hyve-muted); + font-size: 12px; + padding: 10px 16px; +} + +.hyve-next-pagehead { + margin-bottom: 20px; + + h1 { + color: var(--hyve-text); + font-size: 21px; + font-weight: 600; + } + + p { + color: var(--hyve-muted); + font-size: 13px; + margin-top: 3px; + } +} + +.hyve-next-notice { + align-items: center; + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-left: 4px solid var(--hyve-wp-blue); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + display: flex; + gap: 14px; + margin-bottom: 20px; + padding: 12px 16px; + + @media (max-width: 640px) { + flex-wrap: wrap; + } + + &.is-warn { + border-left-color: var(--hyve-warn-dot); + } + + &.is-bad { + border-left-color: var(--hyve-bad-dot); + } + + &.is-compact { + box-shadow: none; + margin: 10px 0 0; + max-width: 420px; + padding: 10px 12px; + } +} + +.hyve-next-notice__body { + flex: 1; + + /* A floor on the text is what lets the action wrap below it. */ + @media (max-width: 640px) { + min-width: 220px; + } +} + +.hyve-next-notice__title { + color: var(--hyve-text); + font-size: 13px; +} + +p.hyve-next-notice__text { + color: var(--hyve-muted); + font-size: 12.5px; + margin-top: 2px; +} + +.hyve-next-stats { + display: grid; + gap: 16px; + grid-template-columns: repeat(4, 1fr); + margin-bottom: 24px; + + @media (max-width: 980px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } +} + +.hyve-next-stat { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + padding: 16px; + + &.is-planned { + background: transparent; + border-color: var(--hyve-border-2); + border-style: dashed; + box-shadow: none; + } +} + +.hyve-next-stat__top { + align-items: center; + color: var(--hyve-muted); + display: flex; + fill: currentcolor; + gap: 8px; +} + +.hyve-next-stat__label { + font-size: 12px; + font-weight: 600; +} + +.hyve-next-stat__num { + color: var(--hyve-text); + font-size: 28px; + font-variant-numeric: tabular-nums; + font-weight: 600; + letter-spacing: -0.02em; + margin: 10px 0 4px; + + small { + color: var(--hyve-muted); + font-size: 14px; + font-weight: 500; + } + + .is-planned & { + color: var(--hyve-faint); + } +} + +.hyve-next-stat__meter { + background: var(--hyve-surface-2); + border-radius: 20px; + height: 6px; + margin: 8px 0 4px; + overflow: hidden; + + i { + background: var(--hyve-wp-blue); + display: block; + height: 100%; + } +} + +.hyve-next-stat__foot { + color: var(--hyve-muted); + font-size: 12px; + + .components-button.is-link { + font-size: 12px; + } +} + +.hyve-next-grid2 { + display: grid; + gap: 16px; + grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); + + @media (max-width: 980px) { + grid-template-columns: minmax(0, 1fr); + } + + > .hyve-next-card { + display: flex; + flex-direction: column; + margin-bottom: 0; + min-width: 0; + } + + .hyve-next-act__row { + align-items: center; + flex: 1; + } + + .hyve-next-act__note { + margin: auto 0; + } +} + +.hyve-next-chart { + height: 320px; + padding: 16px; + position: relative; + + @media (max-width: 640px) { + height: 240px; + } +} + +.hyve-next-card__note { + color: var(--hyve-muted); + font-size: 12.5px; + padding: 24px 16px; } -.components-panel__row { - display: block; - margin: 0; - padding: 16px; +.hyve-next-act__note { + color: var(--hyve-muted); + font-size: 12.5px; + padding: 24px 16px; + text-align: center; } -.components-snackbar-list { - bottom: .5rem; - position: fixed; - padding-left: 1.5rem; +.hyve-next-act__row { + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 11px; + padding: 12px 16px; + + &:last-child { + border-bottom: 0; + } +} + +.hyve-next-act__body { + flex: 1; + min-width: 0; +} + +.hyve-next-act__msg { + color: var(--hyve-text); + display: block; + font-size: 13px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hyve-next-act__meta { + color: var(--hyve-muted); + display: block; + font-size: 11.5px; + margin-top: 2px; +} + +.hyve-next-act__when { + color: var(--hyve-muted); + font-size: 11.5px; + margin-left: auto; + white-space: nowrap; +} + +.hyve-next-act__upsell { + background: #f0f6fc; + border-top: 1px solid #c5d9ec; + margin-top: auto; + padding: 14px 16px; + + strong { + color: var(--hyve-text); + display: block; + font-size: 13px; + } + + p { + color: var(--hyve-text-2); + font-size: 12.5px; + margin: 2px 0 10px; + } +} + +.hyve-next-backrow { + margin-bottom: 14px; +} + +.hyve-next-token { + font-family: Consolas, Monaco, monospace; + font-size: 12px; +} + +.hyve-next-code { + background: var(--hyve-surface-2); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius-in); + color: var(--hyve-text-2); + font-family: Consolas, Monaco, monospace; + font-size: 12px; + line-height: 1.6; + margin: 14px 0 0; + overflow-x: auto; + padding: 12px 14px; +} + +.hyve-next-table-wrap { + overflow-x: auto; +} + +.hyve-next-table { + border-collapse: collapse; + width: 100%; + + th { + border-bottom: 1px solid var(--hyve-border); + color: var(--hyve-muted); + font-size: 11.5px; + font-weight: 600; + letter-spacing: 0.02em; + padding: 10px 16px; + text-align: left; + text-transform: uppercase; + + @media (max-width: 782px) { + padding: 10px 12px; + } + } + + td { + border-bottom: 1px solid var(--hyve-border); + padding: 12px 16px; + vertical-align: middle; + + @media (max-width: 782px) { + padding: 10px 12px; + } + } + + tr:last-child td { + border-bottom: 0; + } + + tbody tr:hover td { + background: var(--hyve-surface-2); + } +} + +.hyve-next-table__main { + max-width: 480px; +} + +/* Modal tables are narrower; a tighter cap keeps the actions in view. */ +.hyve-next-modal .hyve-next-table__main { + max-width: 400px; +} + +/* Phones: a tighter cap keeps status and actions mostly in view. */ +@media (max-width: 640px) { + + .hyve-next-table__main, + .hyve-next-modal .hyve-next-table__main { + max-width: 260px; + } +} + +.hyve-next-table__title { + color: var(--hyve-text); + display: block; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hyve-next-table__sub { + color: var(--hyve-muted); + display: block; + font-size: 12px; + margin-top: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hyve-next-table__num { + font-variant-numeric: tabular-nums; +} + +.hyve-next-table__actions { + text-align: right; +} + +.hyve-next-table__more { + border-top: 1px solid var(--hyve-border); + padding: 12px 16px; + text-align: center; +} + +.hyve-next-src { + display: grid; + gap: 14px; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); +} + +.hyve-next-src__card { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + cursor: pointer; + padding: 16px; + position: relative; + text-align: left; + transition: border-color 0.12s; + + &:hover, + &:focus-visible { + border-color: var(--hyve-wp-blue); + } + + .hyve-next-chip { + position: absolute; + right: 13px; + top: 13px; + } + + h4 { + color: var(--hyve-text); + font-size: 13px; + font-weight: 600; + margin: 0 0 3px; + } + + p { + color: var(--hyve-muted); + font-size: 12px; + margin: 0; + } +} + +.hyve-next-src__icon { + align-items: center; + background: #f0f6fc; + border-radius: var(--hyve-radius); + color: var(--hyve-wp-blue); + display: flex; + height: 34px; + justify-content: center; + margin-bottom: 11px; + width: 34px; + + :where(svg) { + fill: currentcolor; + height: 22px; + width: 22px; + } +} + +.hyve-next__subbadge { + background: var(--hyve-warn-bg); + border: 1px solid #eadb9a; + border-radius: 20px; + color: var(--hyve-warn); + font-size: 11px; + font-variant-numeric: tabular-nums; + font-weight: 700; + margin-left: 6px; + padding: 0 6px; +} + +/* Non-interactive preview of a pro surface, shown above its upsell. */ +.hyve-next-preview { + opacity: 0.75; + pointer-events: none; + user-select: none; +} + +.hyve-next-toolbar { + align-items: center; + display: flex; + gap: 10px; + + > :first-child { + min-width: 160px; + } + + @media (max-width: 782px) { + flex-wrap: wrap; + } +} + +.hyve-next-toolbar__grow { + flex: 1; +} + +.hyve-next-toolbar__count { + color: var(--hyve-muted); + font-size: 12px; + white-space: nowrap; +} + +.hyve-next-bulkbar { + align-items: center; + background: #f0f6fc; + border: 1px solid #c5d9ec; + border-radius: var(--hyve-radius); + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 12px; + padding: 8px 8px 8px 14px; + + strong { + color: var(--hyve-text); + flex: 1; + font-size: 12.5px; + min-width: 140px; + } +} + +.hyve-next-table__check { + width: 36px; + + input { + accent-color: var(--hyve-wp-blue); + cursor: pointer; + margin: 0; + } +} + +.hyve-next-thread__meta { + border-bottom: 1px solid var(--hyve-border); + color: var(--hyve-muted); + font-size: 12px; + padding: 10px 16px; +} + +.hyve-next-thread { + display: flex; + flex-direction: column; + max-height: 60vh; + overflow-y: auto; + padding: 16px; +} + +.hyve-next-pagination { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 8px; } -.components-toggle-group-control { - max-width: 12rem; +.hyve-next-pagination__label { + color: var(--hyve-muted); + font-size: 12px; + font-variant-numeric: tabular-nums; + padding: 0 4px; } -.components-color-palette__custom-color-text-wrapper { - display: none; +.hyve-next-modal__actions { + display: flex; + gap: 8px; + justify-content: flex-end; + margin-top: 20px; + + @media (max-width: 480px) { + flex-wrap: wrap; + } +} + +/* Pushes actions after it to the right edge (e.g. a left-aligned Delete). */ +.hyve-next-modal__spacer { + flex: 1; } -.hyve-chat-message { +/* Caps long tables inside modals (e.g. the sitemap pages). */ +.hyve-next-modal__scroll { + max-height: 340px; + overflow-y: auto; +} + +.hyve-next-bubble { + --hyve-bubble-bg: #ecf1fb; + --hyve-bubble-color: var(--hyve-text); + + display: flex; + flex-direction: column; + margin: 6px 0; + max-width: 75%; + p { + background: var(--hyve-bubble-bg); + border-radius: 6px; + color: var(--hyve-bubble-color); font-size: 13px; + overflow-wrap: anywhere; + padding: 9px 12px; + } + + time { + color: var(--hyve-muted); + font-size: 10.5px; + margin-top: 3px; + padding: 0 2px; + } + + &.is-user { + --hyve-bubble-bg: var(--hyve-brand-b); + --hyve-bubble-color: #fff; + + align-items: flex-end; + margin-left: auto; + } + + &.is-bot { + align-items: flex-start; + margin-right: auto; + } +} + +.hyve-next-card__body { + padding: 16px; + + :where(p) { + margin: 1em 0; + } + + :where(p:first-child) { + margin-top: 0; + } + + :where(p:last-child) { + margin-bottom: 0; + } +} + +.hyve-next-card__intro { + border-bottom: 1px solid var(--hyve-border); + padding: 16px; + + :where(p + p) { + margin-top: 8px; + } +} + +/* Qualified with the element so the margin survives the .hyve-next p reset. */ +p.hyve-next-card__hint { + color: var(--hyve-muted); + font-size: 12px; + margin-top: 14px; +} + +/* Quiet warning variant for inline notes that do not warrant a notice box. */ +p.hyve-next-card__hint.is-warn { + color: var(--hyve-warn); +} + +/* Inside a stack the flex gap already provides the spacing. */ +.hyve-next-stack p.hyve-next-card__hint { + margin-top: 0; +} + +.hyve-next-rcards { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.hyve-next-rcard { + align-items: center; + background: var(--hyve-surface); + border: 1px solid var(--hyve-border-2); + border-radius: var(--hyve-radius-in); + color: var(--hyve-text); + cursor: pointer; + display: inline-flex; + font-size: 13px; + font-weight: 500; + gap: 9px; + padding: 10px 14px; + + input { + accent-color: var(--hyve-wp-blue); + cursor: pointer; + height: 16px; + margin: 0; + width: 16px; + } + + &.is-selected { + background: #f0f6fc; + border-color: var(--hyve-wp-blue); + box-shadow: 0 0 0 1px var(--hyve-wp-blue); } +} - h1, h2, h3, h4, h5, h6 { +.hyve-next-rules { + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + margin-top: 16px; + padding: 14px 16px; + + strong { + color: var(--hyve-text); + display: block; font-size: 13px; - margin-bottom: 12px; } - ol, ul { - padding-left: 12px; + p { + color: var(--hyve-muted); + font-size: 12px; + margin: 3px 0 12px; + } +} + +.hyve-next-rules__row { + display: flex; + gap: 8px; + margin-bottom: 8px; + max-width: 560px; + + > :first-child { + flex: 1; + } +} + +.hyve-next-stack { + display: flex; + flex-direction: column; + gap: 8px; +} + +.hyve-next-buttons { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +/* Scrollable link list inside modals (sitemap pages and similar). */ +.hyve-next-linklist { + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius-in); + display: flex; + flex-direction: column; + gap: 6px; + max-height: 320px; + overflow-y: auto; + padding: 12px 14px; + + :where(p), + .components-checkbox-control__label { + font-size: 12.5px; + overflow-wrap: anywhere; + } +} + +/* Grouped row actions keep the table's right alignment. */ +.hyve-next-table__actions .hyve-next-buttons { + flex-wrap: nowrap; + justify-content: flex-end; +} + +/* Preview rows read as sample data, not real content. */ +.hyve-next-preview .hyve-next-table__title { + color: var(--hyve-faint); + font-weight: 400; +} + +.hyve-next-endpoint { + color: var(--hyve-text); + font-size: 12.5px; + margin-right: 2px; +} + +.hyve-next-meter { + background: var(--hyve-surface-2); + border-radius: 20px; + height: 6px; + margin: 10px 0 6px; + max-width: 420px; + overflow: hidden; + + i { + background: var(--hyve-wp-blue); + display: block; + height: 100%; + } +} + +.hyve-next-modrows { + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + margin: 14px 0; +} + +.hyve-next-modrow { + align-items: center; + border-bottom: 1px solid var(--hyve-border); + display: flex; + gap: 14px; + padding: 8px 14px; + + &:last-child { + border-bottom: 0; + } + + .hyve-next-meter { + flex: 1; + margin: 0; + max-width: none; + } + + @media (max-width: 480px) { + flex-wrap: wrap; + row-gap: 4px; + } +} + +/* Meters span the full modal width (e.g. the sitemap import progress). */ +.hyve-next-modal .hyve-next-meter { + max-width: none; +} + +.hyve-next-modrow__label { + align-items: center; + color: var(--hyve-text); + display: flex; + font-size: 12.5px; + font-weight: 500; + gap: 2px; + width: 220px; + + @media (max-width: 480px) { + width: 100%; + } +} + +.hyve-next-modrow__pct { + font-size: 12px; + font-variant-numeric: tabular-nums; + text-align: right; + width: 40px; +} + +/* Segmented controls follow the mockup: solid WP blue on the active option. */ +.hyve-next .components-toggle-group-control-option-base { + + &[aria-checked="true"], + &[aria-pressed="true"] { + background: var(--hyve-wp-blue); + color: #fff; + + &:hover, + &:focus { + color: #fff; + } + } +} + +.hyve-next-icon-group.components-toggle-group-control { + max-width: none; + + .components-button { + height: 40px; + } + + svg { + height: 24px; + width: 24px; + } +} + +.hyve-next-tiles { + display: grid; + gap: 10px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } +} + +.hyve-next-tile-wrap { + display: block; + min-width: 0; + width: 100%; +} + +.hyve-next-tile { + align-items: center; + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius-in); + cursor: pointer; + display: flex; + font: inherit; + gap: 10px; + padding: 8px 10px; + text-align: left; + width: 100%; + + &:disabled { + cursor: default; + opacity: 0.6; + } + + &:focus-visible, + &:hover:not(:disabled) { + border-color: var(--hyve-wp-blue); + box-shadow: 0 0 0 1px var(--hyve-wp-blue); + outline: none; + } +} + +.hyve-next-tile__swatch { + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: var(--hyve-radius-in); + display: block; + flex: 0 0 auto; + height: 28px; + width: 28px; +} + +.hyve-next-tile__meta { + display: flex; + flex-direction: column; + min-width: 0; +} + +.hyve-next-tile__name { + color: var(--hyve-text); + font-size: 12.5px; + font-weight: 500; + line-height: 1.4; +} + +.hyve-next-tile__hex { + color: var(--hyve-muted); + font-family: ui-monospace, menlo, consolas, monospace; + font-size: 11px; + font-variant-numeric: tabular-nums; + line-height: 1.4; +} + +.hyve-next-icon-preview { + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + display: block; + height: 64px; + margin-bottom: 8px; + object-fit: cover; + width: 64px; +} + +h2.hyve-next-h2 { + color: var(--hyve-text); + font-size: 14px; + font-weight: 600; + margin: 24px 0 16px; +} + +.hyve-next-qa { + display: grid; + gap: 16px; + grid-template-columns: repeat(3, 1fr); + + @media (max-width: 980px) { + grid-template-columns: 1fr 1fr; + } + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } +} + +.hyve-next-qa__item { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + cursor: pointer; + font: inherit; + padding: 16px; + text-align: left; + transition: + border-color 0.12s ease, + box-shadow 0.12s ease; + + &:hover, + &:focus { + border-color: var(--hyve-wp-blue); + box-shadow: 0 1px 6px rgba(34, 113, 177, 0.12); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; } +} + +.hyve-next-qa__icon { + align-items: center; + background: #f0f6fc; + border-radius: var(--hyve-radius); + color: var(--hyve-wp-blue); + display: flex; + fill: currentcolor; + height: 34px; + justify-content: center; + margin-bottom: 12px; + width: 34px; +} + +.hyve-next-qa__title { + color: var(--hyve-text); + display: block; + font-size: 13.5px; + font-weight: 600; + margin-bottom: 4px; +} - pre { - background-color: #282c34; - color: #abb2bf; - padding: 1em; - border-radius: 0; - font-family: monospace; - line-height: 1.5; - overflow-x: auto; - white-space: pre; - word-spacing: normal; - margin: 20px -10px; +.hyve-next-qa__text { + color: var(--hyve-muted); + display: block; + font-size: 12.5px; +} + +.hyve-next__card { + background: var(--hyve-surface); + border: 1px solid var(--hyve-border); + border-radius: var(--hyve-radius); + box-shadow: var(--hyve-shadow); + padding: 24px; - * { - color: #abb2bf; - word-break: normal; - word-wrap: normal; - } + h1 { + color: var(--hyve-text); + font-size: 21px; + font-weight: 600; + } - ::selection { - background-color: #3e4451; - } + p { + color: var(--hyve-muted); + font-size: 13px; + margin-top: 6px; + } +} - ::before { - content: attr(data-content); - } +/* Keyboard focus ring for the custom interactive elements. The transparent + outline keeps a visible indicator in Windows High Contrast mode. Last in + the file so it wins over the hover styles it shares properties with. */ +/* stylelint-disable no-descending-specificity */ +.hyve-next__tab:focus-visible, +.hyve-next__sublink:focus-visible, +.hyve-next__plan:focus-visible, +.hyve-next__doclink:focus-visible, +.hyve-next__cta:focus-visible, +.hyve-next-side__link:focus-visible, +.hyve-next-qa__item:focus-visible, +.hyve-next-src__card:focus-visible, +.hyve-next-tile:focus-visible, +.hyve-next-rcard:has(input:focus-visible) { + box-shadow: + 0 0 0 1px var(--hyve-surface), + 0 0 0 3px var(--hyve-wp-blue); + outline: 2px solid transparent; +} +/* stylelint-enable no-descending-specificity */ - ::before:has(+ :matches(function, const, return, let, var)) { - color: #c678dd; - } - } +/* Admin-page globals, kept from the previous stylesheet. */ +#wpcontent { + padding: 0; +} - code { - background: #d0effb; - border-radius: 5px; - border: none; - padding: 0 3px; - color: #333; - } +.components-snackbar-list { + bottom: 0.5rem; + padding-left: 1.5rem; + position: fixed; } div.notice.themeisle-sale { - margin: 0; - margin-bottom: 1rem; + margin: 0 0 1rem; } -.hyve-chat-icons.components-toggle-group-control { - max-width: none; +/* Chat replies may carry code snippets (transcripts, message review). */ +.hyve-next-bubble { + pre { + background-color: #282c34; + border-radius: 4px; + color: #abb2bf; + font-family: monospace; + line-height: 1.5; + margin: 12px 0; + overflow-x: auto; + padding: 1em; + white-space: pre; + word-spacing: normal; + + * { + color: #abb2bf; + word-break: normal; + word-wrap: normal; + } + + ::selection { + background-color: #3e4451; + } + } + + code { + background: #d0effb; + border: none; + border-radius: 5px; + color: #333; + padding: 0 3px; + } } -.service-error.components-notice.is-error { - background-color: #fabbbb; // Make the contrast ratio higher. -} \ No newline at end of file diff --git a/src/backend/utils.js b/src/backend/utils.js index dd277502..660ce413 100644 --- a/src/backend/utils.js +++ b/src/backend/utils.js @@ -152,10 +152,24 @@ export const onProcessData = async ( { throw response; } - createNotice( 'success', __( 'Post has been updated.', 'hyve-lite' ), { - type: 'snackbar', - isDismissible: true, - } ); + if ( response.warning ) { + // Content was added, but indexing reported a problem (e.g. rate + // limit, no credits). Surface it immediately instead of a plain + // success notice. + createNotice( 'warning', response.warning, { + type: 'snackbar', + isDismissible: true, + } ); + } else { + createNotice( + 'success', + __( 'Post has been updated.', 'hyve-lite' ), + { + type: 'snackbar', + isDismissible: true, + } + ); + } onSuccess(); } catch ( error ) { @@ -196,6 +210,24 @@ export const setUtm = ( urlAdress, linkArea ) => { return urlLink.toString(); }; +/** + * Whether the license currently unlocks Pro features. + * + * Mirrors the server-side rule: a valid or expired-but-activated license + * keeps features; a missing, invalid or deactivated one locks them. The Pro + * plugin being installed is not enough on its own. + * + * @return {boolean} Whether Pro features are unlocked. + */ +export const isLicenseActive = () => { + const license = window.hyve?.license || {}; + + return ( + [ 'valid', 'active_expired' ].includes( license.valid ) || + 'valid' === license.license + ); +}; + export const getChatIcons = () => [ { icon: ChatBubbleLeftEllipsisIcon, diff --git a/src/block/index.js b/src/block/index.js index e2166ab8..0f566f80 100644 --- a/src/block/index.js +++ b/src/block/index.js @@ -15,16 +15,26 @@ import { Placeholder, Button, Notice } from '@wordpress/components'; import metadata from './block.json'; registerBlockType( metadata.name, { - edit: () => { + edit: ( { attributes } ) => { // eslint-disable-next-line react-hooks/rules-of-hooks const blockProps = useBlockProps(); const isKnowledgeBaseEmpty = 0 === Number( window.hyveChatBlock.stats.totalChunks ?? '0' ); - const isBlockIgnored = Boolean( - window.hyveChatBlock?.globalChatEnabled + const isGlobalChat = Boolean( window.hyveChatBlock?.globalChatEnabled ); + const isFloatingVariant = 'floating' === attributes?.variant; + let placeholderText = __( + 'Hyve Chatbot will appear here. No further action needed.', + 'hyve-lite' ); + if ( isFloatingVariant ) { + placeholderText = __( + 'Hyve Chatbot bubble will appear on this page. No further action needed.', + 'hyve-lite' + ); + } + return (
@@ -59,38 +69,27 @@ registerBlockType( metadata.name, {

) } - { isBlockIgnored ? ( -
- { __( - 'The Hyve Chat is enabled on all pages, and it won’t appear here to avoid conflicts.', - 'hyve-lite' - ) } - -
- ) : ( - __( - 'Hyve Chatbot will appear here. No further action needed.', - 'hyve-lite' - ) + { isGlobalChat && ! isFloatingVariant && ( + +

+ { __( + 'Hyve Chat is set to appear on all pages. This inline block will be shown here in place of the floating bubble.', + 'hyve-lite' + ) } +

+
+ ) } + { isGlobalChat && isFloatingVariant && ( + +

+ { __( + 'Hyve Chat already appears on all pages, so this floating block is not needed here.', + 'hyve-lite' + ) } +

+
) } + { placeholderText }
); diff --git a/src/frontend/App.js b/src/frontend/App.js index fe55c06e..e4093fc7 100644 --- a/src/frontend/App.js +++ b/src/frontend/App.js @@ -7,10 +7,13 @@ import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; -const clickAudio = new Audio( window.hyveClient.audio.click ); const pingAudio = new Audio( window.hyveClient.audio.ping ); const { strings } = window.hyveClient; +// Default assistant avatar, reused by the header and the live preview refresh. +const ROBOT_AVATAR_SVG = + ''; + class App { constructor() { this.isInitialToggle = true; @@ -20,20 +23,37 @@ class App { this.runID = null; this.recordID = null; this.isMenuOpen = false; + this.isInline = false; + this.teaserTimeout = null; + this.teaserExitListener = null; + this.teaserScrollListener = null; if ( Boolean( window.hyveClient?.canShow ) ) { this.initialize(); } } - async initialize() { + initialize() { this.restoreStorage(); - await this.renderUI(); + this.renderUI(); this.setupListeners(); this.restoreMessages(); + this.setupProactive(); + + // The inline block is always visible, so there's no open action to + // trigger the greeting — show it on load instead. + if ( this.isInline ) { + this.maybeShowWelcome(); + } } restoreStorage() { + // In the admin live preview we keep the test conversation ephemeral so it + // never mixes with the visitor's real saved chat on the same origin. + if ( this.isPreview() ) { + return; + } + const storageData = window.localStorage.getItem( 'hyve-chat' ); if ( null === storageData ) { @@ -94,6 +114,10 @@ class App { } updateStorage() { + if ( this.isPreview() ) { + return; + } + const messages = this.messages .filter( ( message ) => null === message.id ) .slice( -20 ); @@ -109,7 +133,7 @@ class App { ); } - add( message, sender, id = null ) { + add( message, sender, id = null, sound = true ) { const time = new Date(); if ( 'user' === sender ) { @@ -119,7 +143,7 @@ class App { message = this.addTargetBlank( message ); this.messages.push( { time, message, sender, id } ); - this.addMessage( time, message, sender, id ); + this.addMessage( time, message, sender, id, sound ); this.updateStorage(); @@ -127,6 +151,10 @@ class App { return; } + // Sending a message is itself agreement, so retire the privacy notice + // (and remember it) once the visitor's first message goes out. + this.dismissPrivacyNotice(); + this.sendRequest( message ); if ( this.hasSuggestions ) { @@ -206,14 +234,22 @@ class App { async getResponse( message ) { try { + const query = { + thread_id: this.threadID, + run_id: this.runID, + // In preview there is no thread record, so send 0 to satisfy the + // required param; the server skips recording for test chats. + record_id: this.recordID ?? 0, + message, + }; + + if ( this.isPreview() ) { + query.is_test = 1; + } + const response = await apiFetch( { path: this.addCacheProtection( - addQueryArgs( `${ window.hyveClient.api }/chat`, { - thread_id: this.threadID, - run_id: this.runID, - record_id: this.recordID, - message, - } ) + addQueryArgs( `${ window.hyveClient.api }/chat`, query ) ), headers: this.getDefaultHeaders(), } ); @@ -240,25 +276,388 @@ class App { if ( 'completed' === response.status ) { this.add( response.message, 'bot' ); this.setLoading( false ); + + // Contextual follow-ups on the poll path (Pro), mirroring the + // streaming flow. Present only on a successful, grounded answer. + this.renderSuggestions( response.follow_ups ); } if ( 'failed' === response.status ) { this.add( strings.tryAgain, 'bot' ); this.setLoading( false ); } - } catch ( error ) { + } catch { this.add( strings.tryAgain, 'bot' ); this.setLoading( false ); } } async sendRequest( message ) { + this.setLoading( true ); + this.addPreloaderMessage( 'hyve-preloader' ); + + // Try real streaming first; fall back to the poll flow when the host + // buffers the response or streaming isn't available. + if ( this.canStream() ) { + const handled = await this.streamRequest( message ); + + if ( handled ) { + return; + } + } + + await this.backgroundRequest( message ); + } + + /** + * Whether progressive streaming should be attempted. + * + * @return {boolean} True when streaming can be attempted. + */ + canStream() { + return ( + Boolean( window.hyveClient?.ajaxUrl ) && + Boolean( window.hyveClient?.streamNonce ) && + 'function' === typeof window.fetch && + 'undefined' !== typeof window.AbortController && + 'undefined' !== typeof window.TextDecoder && + ! this.streamRecentlyUnsupported() + ); + } + + /** + * Whether streaming was marked unsupported within the last 24h. + * + * The flag is time-bounded so a one-off slow start or transient blip can't + * permanently disable streaming for the visitor — it is re-probed after a day. + * + * @return {boolean} True if streaming should be skipped for now. + */ + streamRecentlyUnsupported() { try { - this.setLoading( true ); + const value = window.localStorage.getItem( + 'hyve-stream-unsupported' + ); + + if ( ! value ) { + return false; + } + + const ts = parseInt( value, 10 ); + const DAY = 24 * 60 * 60 * 1000; + + if ( isNaN( ts ) || Date.now() - ts > DAY ) { + window.localStorage.removeItem( 'hyve-stream-unsupported' ); + return false; + } + + return true; + } catch { + return false; + } + } + + /** + * Remember (with a timestamp) that streaming is unavailable on this host so + * later messages skip straight to the poll flow, re-probing after 24h. + */ + markStreamUnsupported() { + try { + window.localStorage.setItem( + 'hyve-stream-unsupported', + String( Date.now() ) + ); + } catch {} + } + + /** + * Parse a single SSE event block into its event name and JSON payload. + * + * @param {string} raw The raw event block (lines between blank lines). + * @return {{event:string,data:any}} Parsed event. + */ + parseSSE( raw ) { + let event = ''; + const dataLines = []; + + raw.split( '\n' ).forEach( ( line ) => { + if ( line.startsWith( 'event:' ) ) { + event = line.slice( 6 ).trim(); + } else if ( line.startsWith( 'data:' ) ) { + dataLines.push( line.slice( 5 ).replace( /^ /, '' ) ); + } + } ); + + let data = null; + + if ( dataLines.length ) { + try { + data = JSON.parse( dataLines.join( '\n' ) ); + } catch { + data = null; + } + } + + return { event, data }; + } + + /** + * Attempt a streamed reply. + * + * @param {string} message The user's message. + * @return {Promise} True if the reply was handled (shown or a real + * error surfaced); false to fall back to polling. + */ + async streamRequest( message ) { + let token; + + try { + const setup = await apiFetch( { + path: this.addCacheProtection( + `${ window.hyveClient.api }/chat` + ), + method: 'POST', + data: { + message, + mode: 'stream', + ...( null !== this.threadID + ? { thread_id: this.threadID } + : {} ), + ...( null !== this.recordID + ? { record_id: this.recordID } + : {} ), + ...( this.isPreview() ? { is_test: true } : {} ), + }, + headers: this.getDefaultHeaders(), + } ); + + if ( setup.error ) { + // A real content/server error (e.g. flagged) — not a transport + // problem, so surface it instead of falling back. + this.removeMessage( 'hyve-preloader' ); + this.add( strings.tryAgain, 'bot' ); + this.setLoading( false ); + return true; + } + + if ( ! setup.stream_token ) { + return false; + } + + token = setup.stream_token; + + if ( setup.thread_id && setup.thread_id !== this.threadID ) { + this.setThreadID( setup.thread_id ); + } + + if ( + undefined !== setup.record_id && + setup.record_id !== this.recordID + ) { + this.setRecordID( setup.record_id ); + } + } catch { + return false; + } + + const url = addQueryArgs( window.hyveClient.ajaxUrl, { + action: 'hyve_stream', + token, + nonce: window.hyveClient.streamNonce, + } ); + + const controller = new AbortController(); + const bubbleId = 'hyve-stream'; + + let firstEvent = false; + let timedOut = false; + let started = false; + let streamedText = ''; + + // Fall back if no real SSE event (delta/done/error) arrives in time. The + // ': connected' comment is deliberately NOT counted as content, so a proxy + // that forwards the comment but buffers the body is still detected. 8s is + // lenient enough for a slow first token; a false trip self-heals after 24h. + const watchdog = setTimeout( () => { + if ( ! firstEvent ) { + timedOut = true; + controller.abort(); + } + }, 8000 ); + + const renderInto = ( html ) => { + const node = document.getElementById( + `hyve-message-${ bubbleId }` + ); + + if ( ! node ) { + return; + } + + const inner = node.querySelector( 'div' ); + + if ( inner ) { + inner.innerHTML = html; + } + + const box = document.getElementById( 'hyve-message-box' ); + + if ( box ) { + box.scrollTop = box.scrollHeight; + } + }; + + const ensureBubble = () => { + if ( started ) { + return; + } + + this.removeMessage( 'hyve-preloader' ); + this.addMessage( new Date(), '', 'bot', bubbleId, false ); + started = true; + }; + + try { + const response = await fetch( url, { + headers: { Accept: 'text/event-stream' }, + credentials: 'same-origin', + signal: controller.signal, + } ); + + if ( ! response.ok || ! response.body ) { + clearTimeout( watchdog ); + return false; + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + let buffer = ''; + let finished = false; + let reading = true; + + while ( reading ) { + const { value, done } = await reader.read(); + + if ( done ) { + break; + } + + buffer += decoder.decode( value, { stream: true } ); + + let sep = buffer.indexOf( '\n\n' ); + + while ( -1 !== sep ) { + const rawEvent = buffer.slice( 0, sep ); + buffer = buffer.slice( sep + 2 ); + sep = buffer.indexOf( '\n\n' ); + + const { event, data } = this.parseSSE( rawEvent ); + + if ( ! event ) { + continue; + } + + // First real content event: the pipe is flushing, cancel the + // fallback watchdog. + if ( ! firstEvent ) { + firstEvent = true; + clearTimeout( watchdog ); + } + + if ( 'delta' === event ) { + ensureBubble(); + streamedText += data?.text || ''; + renderInto( streamedText ); + } else if ( 'done' === event ) { + this.finalizeStream( bubbleId, data ); + finished = true; + reading = false; + break; + } else if ( 'error' === event ) { + // Generation failed and the server recorded nothing. + // Discard any partial and fall back to the poll flow for a + // complete, recorded-once answer. + clearTimeout( watchdog ); + this.removeMessage( bubbleId ); + this.removeMessage( 'hyve-preloader' ); + this.addPreloaderMessage( 'hyve-preloader' ); + return false; + } + } + } - const preloaderId = 'hyve-preloader'; - this.addPreloaderMessage( preloaderId ); + clearTimeout( watchdog ); + if ( finished ) { + return true; + } + + // Stream ended without a terminal event. + if ( started ) { + this.finalizeStream( bubbleId, { + success: true, + message: streamedText, + } ); + return true; + } + + return false; + } catch { + clearTimeout( watchdog ); + + if ( started ) { + this.finalizeStream( bubbleId, { + success: true, + message: streamedText, + } ); + return true; + } + + if ( timedOut ) { + this.markStreamUnsupported(); + } + + return false; + } + } + + /** + * Replace the streaming placeholder with the final, authoritative reply. + * + * @param {string} bubbleId The temporary streaming bubble id. + * @param {any} data The `done` payload ({ success, message }). + */ + finalizeStream( bubbleId, data ) { + this.removeMessage( 'hyve-preloader' ); + this.removeMessage( bubbleId ); + + // The streamed turn is recorded server-side only once the reply lands, + // so adopt the returned record id to keep follow-ups on the same thread. + if ( + undefined !== data?.record_id && + null !== data?.record_id && + data.record_id !== this.recordID + ) { + this.setRecordID( data.record_id ); + } + + const message = data?.message ?? strings.tryAgain; + this.add( message, 'bot' ); + this.setLoading( false ); + // Contextual follow-ups ride on the terminal event (Pro). They are only + // present on a successful, grounded answer. + this.renderSuggestions( data?.follow_ups ); + } + + /** + * Background reply flow: create a run and poll for the answer. This is the + * original, proven path, used as the fallback when streaming is unavailable. + * + * @param {string} message The user's message. + */ + async backgroundRequest( message ) { + try { const response = await apiFetch( { path: this.addCacheProtection( `${ window.hyveClient.api }/chat` @@ -272,14 +671,20 @@ class App { ...( null !== this.recordID ? { record_id: this.recordID } : {} ), + ...( this.isPreview() ? { is_test: true } : {} ), }, headers: this.getDefaultHeaders(), } ); - this.removeMessage( preloaderId ); + this.removeMessage( 'hyve-preloader' ); if ( response.error ) { - this.add( strings.tryAgain, 'bot' ); + this.add( + 'content_flagged' === response.code + ? strings.flagged + : strings.tryAgain, + 'bot' + ); this.setLoading( false ); return; } @@ -294,10 +699,12 @@ class App { this.setRunID( response.query_run ); - this.add( strings.typing, 'bot', response.query_run ); + // Keep the animated typing indicator (not a plain "Typing…" text) + // visible while we poll for the answer. + this.addPreloaderMessage( response.query_run ); await this.getResponse( message ); - } catch ( error ) { + } catch { this.removeMessage( 'hyve-preloader' ); this.add( strings.tryAgain, 'bot' ); this.setLoading( false ); @@ -305,7 +712,103 @@ class App { } addAudioPlayback( audioElement ) { - audioElement.play(); + if ( ! this.isSoundEnabled() ) { + return; + } + + // Autoplay policies reject play() before the visitor interacts with + // the page (e.g. a proactive teaser on page load); stay silent then. + audioElement.play()?.catch( () => {} ); + } + + /** + * Whether the visitor has muted the chat sound in this browser. + * + * @return {boolean} True if muted. + */ + isMuted() { + return 'true' === window.localStorage.getItem( 'hyve-sound-muted' ); + } + + /** + * Whether sound is enabled globally (by the site admin). + * + * `wp_localize_script` casts booleans to strings ('1' for true, '' for + * false), so we treat an empty string or explicit false as disabled and a + * missing value as enabled. + * + * @return {boolean} True if sound is allowed site-wide. + */ + isGlobalSoundEnabled() { + const value = window.hyveClient?.soundEnabled; + return false !== value && '' !== value; + } + + /** + * Whether sound should play — enabled globally and not muted by the visitor. + * + * @return {boolean} True if sound should play. + */ + isSoundEnabled() { + if ( ! this.isGlobalSoundEnabled() ) { + return false; + } + + return ! this.isMuted(); + } + + /** + * Whether message timestamps should be shown. + * + * `wp_localize_script` casts booleans to strings ('1' / ''), so an empty + * string or explicit false hides the timestamp; a missing value shows it. + * + * @return {boolean} True if timestamps should render. + */ + isTimestampVisible() { + const value = window.hyveClient?.showTimestamp; + return false !== value && '' !== value; + } + + /** + * Whether the widget is running inside the admin Appearance live preview. + * + * @return {boolean} True in preview mode. + */ + isPreview() { + return Boolean( window.hyveClient?.isPreview ); + } + + /** + * Markup (icon + label) for the sound toggle menu item, reflecting state. + * + * @return {string} The inner HTML for the toggle button. + */ + soundMenuInner() { + const icon = this.isMuted() + ? '' + : ''; + + const label = this.isMuted() ? strings.unmuteSound : strings.muteSound; + + return `${ icon } ${ label }`; + } + + /** + * Toggle the per-visitor sound preference and refresh the menu item. + * + * @return {void} + */ + toggleSound() { + window.localStorage.setItem( + 'hyve-sound-muted', + this.isMuted() ? 'false' : 'true' + ); + + const soundButton = document.getElementById( 'hyve-toggle-sound' ); + if ( soundButton ) { + soundButton.innerHTML = this.soundMenuInner(); + } } addMessage( time, message, sender, id, sound = true ) { @@ -318,7 +821,7 @@ class App { let messageHTML = `
${ message }
`; - if ( null === id ) { + if ( null === id && this.isTimestampVisible() ) { messageHTML += ``; } @@ -379,9 +882,17 @@ class App { } if ( isOpen ) { + // The visitor found the chat; the teaser's job is done. + this.removeTeaser(); + openButton.style.display = 'none'; closeButton.style.display = 'block'; - chatWindow.style.display = 'block'; + chatWindow.style.display = 'flex'; + + // Trigger the entrance animation on the next frame. + window.requestAnimationFrame( () => { + chatWindow.classList.add( 'is-open' ); + } ); const chatMessageBox = document.getElementById( 'hyve-message-box' ); @@ -392,10 +903,19 @@ class App { openButton.style.display = 'block'; closeButton.style.display = 'none'; chatWindow.style.display = 'none'; + chatWindow.classList.remove( 'is-open' ); } - this.addAudioPlayback( clickAudio ); + this.maybeShowWelcome(); + } + /** + * Show the welcome message and suggested questions once, when the chat is + * first shown with no prior conversation. + * + * @return {void} + */ + maybeShowWelcome() { if ( window.hyveClient.welcome && '' !== window.hyveClient.welcome && @@ -413,31 +933,55 @@ class App { } addSuggestions() { - const questions = window.hyveClient?.predefinedQuestions; + this.renderSuggestions( window.hyveClient?.predefinedQuestions ); + } + /** + * Render a row of clickable suggestion chips under the latest message. + * + * Shared by the pre-conversation predefined questions and the per-turn + * follow-up questions. Clicking a chip sends it as the next message; any + * previous chip row is cleared first so only the latest set is shown. + * + * @param {Array} questions The suggestions to render. + * @return {void} + */ + renderSuggestions( questions ) { if ( ! Array.isArray( questions ) ) { return; } - const filteredQuestions = questions.filter( - ( question ) => '' !== question.trim() - ); + const filteredQuestions = questions + .filter( ( question ) => 'string' === typeof question ) + .map( ( question ) => question.trim() ) + .filter( ( question ) => '' !== question ); if ( 0 === filteredQuestions.length ) { return; } - const chatMessageBox = document.getElementById( 'hyve-message-box' ); - - const suggestions = [ `${ strings.suggestions }` ]; + // Clear any prior chip row so only the latest set is on screen. + this.removeSuggestions(); - filteredQuestions.forEach( ( question ) => { - suggestions.push( `` ); - } ); + const chatMessageBox = document.getElementById( 'hyve-message-box' ); const messageDiv = this.createElement( 'div', { className: 'hyve-suggestions', - innerHTML: suggestions.join( '' ), + } ); + + const label = this.createElement( 'span' ); + label.textContent = strings.suggestions; + messageDiv.appendChild( label ); + + // Build buttons with textContent (not innerHTML) so model-generated + // follow-ups cannot inject markup into the widget. + filteredQuestions.forEach( ( question ) => { + const button = this.createElement( 'button' ); + button.textContent = question; + button.addEventListener( 'click', () => { + this.add( question, 'user' ); + } ); + messageDiv.appendChild( button ); } ); if ( window.hyveClient.colors?.user_background ) { @@ -451,16 +995,14 @@ class App { messageDiv.classList.add( 'is-light' ); } - const suggestionButtons = messageDiv.querySelectorAll( 'button' ); - - suggestionButtons.forEach( ( button ) => { - button.addEventListener( 'click', () => { - this.add( button.textContent, 'user' ); - } ); - } ); - chatMessageBox?.appendChild( messageDiv ); + // The reply already scrolled to its own bottom before the chips were + // appended, so bring the freshly added chips into view too. + if ( chatMessageBox ) { + chatMessageBox.scrollTop = chatMessageBox.scrollHeight; + } + this.hasSuggestions = true; } @@ -551,6 +1093,13 @@ class App { } ); } + const soundButton = document.getElementById( 'hyve-toggle-sound' ); + if ( soundButton ) { + soundButton.addEventListener( 'click', () => { + this.toggleSound(); + } ); + } + // Close menu when clicking outside document.addEventListener( 'click', ( event ) => { const menu = document.querySelector( '.hyve-menu-dropdown' ); @@ -608,29 +1157,430 @@ class App { }; } - async renderUI() { + /** + * Whether the visitor has dismissed the privacy notice in this browser. + * + * Dismissal is never persisted in the admin preview, so admins always see + * the notice while configuring the widget. + * + * @return {boolean} True if the notice was dismissed. + */ + isPrivacyNoticeDismissed() { + if ( this.isPreview() ) { + return false; + } + + try { + return ( + 'true' === + window.localStorage.getItem( 'hyve-privacy-dismissed' ) + ); + } catch { + return false; + } + } + + /** + * Remember that the visitor dismissed the privacy notice and remove it. + * + * @return {void} + */ + dismissPrivacyNotice() { + const notice = document.querySelector( '.hyve-privacy-notice' ); + + if ( notice ) { + notice.remove(); + } + + // The "Powered by Hyve" credit is kept hidden while the notice shows + // (free version only); reveal it now that the notice is gone. + const credits = document.querySelector( '.hyve-credits' ); + + if ( credits ) { + credits.hidden = false; + } + + if ( this.isPreview() ) { + return; + } + + try { + window.localStorage.setItem( 'hyve-privacy-dismissed', 'true' ); + } catch {} + } + + /** + * Arm the proactive teaser: a configurable invite shown next to the closed + * launcher when the selected trigger fires (Pro supplies the config through + * `hyveClient.proactive`). UI only — no thread or API call happens until + * the visitor actually sends a message. + * + * @return {void} + */ + setupProactive() { + const config = window.hyveClient?.proactive; + + if ( ! config?.message || this.isInline || this.isPreview() ) { + return; + } + + // The teaser anchors to the floating launcher. + if ( ! document.getElementById( 'hyve-open' ) ) { + return; + } + + // A returning visitor with a conversation doesn't need an invite. + if ( this.threadID || this.hasUserMessages() ) { + return; + } + + if ( this.isTeaserSuppressed() ) { + return; + } + + this.armTeaserTrigger( config ); + } + + /** + * Whether the teaser should stay hidden: dismissed in this browser, or + * already shown once this session. + * + * @return {boolean} True when suppressed. + */ + isTeaserSuppressed() { + try { + return ( + 'true' === + window.localStorage.getItem( 'hyve-teaser-dismissed' ) || + 'true' === window.sessionStorage.getItem( 'hyve-teaser-shown' ) + ); + } catch { + return true; + } + } + + /** + * Arm the configured trigger. Exactly one is active at a time. + * + * @param {Object} config The proactive config. + * @param {string} config.trigger One of 'time', 'exit', 'scroll'. + * @param {string} config.message The teaser message. + * @param {number} [config.delay] Seconds on page (time trigger). + * @param {number} [config.scrollDepth] Percent scrolled (scroll trigger). + * + * @return {void} + */ + armTeaserTrigger( config ) { + const show = () => this.showTeaser( config.message ); + + switch ( config.trigger ) { + case 'exit': + // Desktop only: fires when the pointer leaves toward the top + // of the viewport. Touch devices never emit it, so the teaser + // simply doesn't show there. + this.teaserExitListener = ( event ) => { + if ( ! event.relatedTarget && 0 >= event.clientY ) { + show(); + } + }; + document.addEventListener( + 'mouseout', + this.teaserExitListener + ); + break; + case 'scroll': + this.teaserScrollListener = () => { + const doc = document.documentElement; + const max = doc.scrollHeight - window.innerHeight; + const percent = + 0 < max ? ( window.scrollY / max ) * 100 : 100; + + if ( percent >= ( config.scrollDepth ?? 50 ) ) { + show(); + } + }; + window.addEventListener( 'scroll', this.teaserScrollListener, { + passive: true, + } ); + break; + default: + this.teaserTimeout = window.setTimeout( + show, + 1000 * ( config.delay ?? 0 ) + ); + } + } + + /** + * Cancel any armed teaser trigger. + * + * @return {void} + */ + disarmTeaserTrigger() { + if ( this.teaserTimeout ) { + window.clearTimeout( this.teaserTimeout ); + this.teaserTimeout = null; + } + + if ( this.teaserExitListener ) { + document.removeEventListener( 'mouseout', this.teaserExitListener ); + this.teaserExitListener = null; + } + + if ( this.teaserScrollListener ) { + window.removeEventListener( 'scroll', this.teaserScrollListener ); + this.teaserScrollListener = null; + } + } + + /** + * Show the teaser bubble next to the closed launcher. Clicking the message + * opens the chat (the normal welcome flow proceeds); the X dismisses it + * for good. Focus is never moved — the bubble announces itself politely + * via role="status". + * + * @param {string} message The teaser message. + * + * @return {void} + */ + showTeaser( message ) { + this.disarmTeaserTrigger(); + + // The moment may have passed: the chat could have been opened, or a + // conversation started, while the trigger was armed. + const openButton = document.getElementById( 'hyve-open' ); + + if ( + ! openButton || + 'none' === openButton.style.display || + this.hasUserMessages() || + document.getElementById( 'hyve-teaser' ) + ) { + return; + } + + try { + window.sessionStorage.setItem( 'hyve-teaser-shown', 'true' ); + } catch {} + + this.renderTeaser( message ); + } + + /** + * Show the teaser with a given message in the admin preview widget, + * bypassing triggers and suppression state. Used by the settings screen. + * + * @param {string} message The teaser message to preview. + * + * @return {void} + */ + previewTeaser( message ) { + if ( ! message || this.isInline ) { + return; + } + + const openButton = document.getElementById( 'hyve-open' ); + + if ( ! openButton ) { + return; + } + + // The teaser anchors to the closed launcher, so close the window first. + if ( 'none' === openButton.style.display ) { + this.toggleChatWindow( false ); + } + + this.removeTeaser(); + this.renderTeaser( message ); + } + + /** + * Build and attach the teaser bubble next to the launcher. + * + * @param {string} message The teaser message. + * + * @return {void} + */ + renderTeaser( message ) { + const messageButton = this.createElement( 'button', { + className: 'hyve-teaser__message', + textContent: message, + } ); + + messageButton.addEventListener( 'click', () => { + this.removeTeaser(); + this.toggleChatWindow( true ); + } ); + + const dismissButton = this.createElement( 'button', { + className: 'hyve-teaser__dismiss', + ariaLabel: strings.dismissNotice ?? 'Dismiss', + innerHTML: + '', + } ); + + dismissButton.addEventListener( 'click', () => { + // Never persist dismissal from the admin preview, or previewing + // would suppress the real teaser for the admin as a visitor. + if ( ! this.isPreview() ) { + try { + window.localStorage.setItem( + 'hyve-teaser-dismissed', + 'true' + ); + } catch {} + } + + this.removeTeaser(); + } ); + + const teaser = this.createElement( + 'div', + { className: 'hyve-teaser', id: 'hyve-teaser' }, + messageButton, + dismissButton + ); + + teaser.setAttribute( 'role', 'status' ); + + if ( 'left' === window.hyveClient?.chatPosition ) { + teaser.classList.add( 'is-left' ); + } + + // Match the bot-message contrast so the text stays readable on a dark + // assistant background. + if ( window.hyveClient?.colors?.assistant_background ) { + teaser.classList.add( 'is-dark' ); + } + + document.body.appendChild( teaser ); + + this.addAudioPlayback( pingAudio ); + } + + /** + * Remove the teaser bubble and cancel any armed trigger. + * + * @return {void} + */ + removeTeaser() { + this.disarmTeaserTrigger(); + document.getElementById( 'hyve-teaser' )?.remove(); + } + + /** + * Build the dismissible privacy notice shown above the input box. + * + * The notice text carries a single `%s` placeholder marking where the + * (optional) privacy-policy link goes; both halves are inserted as text + * nodes so admin-supplied copy can never inject markup. + * + * @return {HTMLElement|null} The notice element, or null when it shouldn't show. + */ + renderPrivacyNotice() { + const notice = window.hyveClient?.privacyNotice; + + // Skip entirely without a resolvable policy URL — a notice that points + // nowhere is worse than no notice. The admin dashboard warns when the + // toggle is on but no Privacy Policy page has been set. + if ( + ! notice?.enabled || + ! notice?.url || + ! strings.privacyNotice || + this.isPrivacyNoticeDismissed() + ) { + return null; + } + + const icon = this.createElement( 'span', { + className: 'hyve-privacy-notice__icon', + innerHTML: + '', + } ); + + const text = this.createElement( 'span', { + className: 'hyve-privacy-notice__text', + } ); + + const link = this.createElement( 'a', { + className: 'hyve-privacy-notice__link', + href: notice.url, + target: '_blank', + rel: 'noopener noreferrer', + textContent: strings.privacyPolicy || '', + } ); + + const [ before, after = '' ] = strings.privacyNotice.split( '%s' ); + + text.appendChild( document.createTextNode( before ) ); + text.appendChild( link ); + text.appendChild( document.createTextNode( after ) ); + + const dismiss = this.createElement( 'button', { + className: 'hyve-privacy-notice__dismiss', + ariaLabel: strings.dismissNotice ?? 'Dismiss', + innerHTML: + '', + } ); + + dismiss.addEventListener( 'click', () => this.dismissPrivacyNotice() ); + + const container = this.createElement( + 'div', + { className: 'hyve-privacy-notice' }, + icon, + text, + dismiss + ); + + if ( window.hyveClient.colors?.chat_background ) { + container.classList.add( 'is-dark' ); + } + + return container; + } + renderUI() { + // Whether the widget is anchored to the left side of the screen. + const isLeft = 'left' === window.hyveClient?.chatPosition; + const chatOpenButton = this.createElement( 'button', { className: 'collapsible open', + ariaLabel: strings.openChat, + ariaExpanded: 'true', } ); + const chatIcon = window.hyveClient?.chatIcon; let useDefaultIcon = true; - if ( 'svg' === window.hyveClient?.chatIcon?.type ) { - /** - * NOTE: Download the SVG to that we can use the styling via CSS. - */ - const iconURL = - window.hyveClient?.icons?.[ - window.hyveClient?.chatIcon?.value - ]; - if ( iconURL ) { - const svg = await this.fetchSVG( iconURL ); - chatOpenButton.innerHTML = svg; + + if ( 'media' === chatIcon?.type && chatIcon?.url ) { + chatOpenButton.appendChild( + this.createElement( 'img', { + className: 'hyve-icon-img', + src: chatIcon.url, + alt: '', + } ) + ); + useDefaultIcon = false; + } else if ( 'svg' === chatIcon?.type ) { + // SVG markup is inlined server-side so it renders instantly (no fetch). + const iconMarkup = window.hyveClient?.icons?.[ chatIcon?.value ]; + if ( iconMarkup ) { + chatOpenButton.innerHTML = iconMarkup; useDefaultIcon = false; } } if ( useDefaultIcon ) { - chatOpenButton.appendChild( document.createTextNode( '💬' ) ); + // Default icon: the bundled chat-bubble SVG (matches the option + // shown as the default in the admin). Emoji is a last-resort fallback. + const defaultIcon = + window.hyveClient?.icons?.[ 'chat-bubble-left-ellipsis' ]; + + if ( defaultIcon ) { + chatOpenButton.innerHTML = defaultIcon; + } else { + chatOpenButton.appendChild( document.createTextNode( '💬' ) ); + } } const chatOpen = this.createElement( @@ -639,10 +1589,28 @@ class App { chatOpenButton ); + if ( isLeft ) { + chatOpen.classList.add( 'is-left' ); + } + + // Adapt the open icon color to the icon background, like the close icon. + if ( window.hyveClient.colors?.icon_background ) { + chatOpen.classList.add( 'is-dark' ); + } + + if ( + ! window.hyveClient.colors?.icon_background && + undefined !== window.hyveClient.colors?.icon_background + ) { + chatOpen.classList.add( 'is-light' ); + } + const chatCloseButton = this.createElement( 'button', { className: 'collapsible close', innerHTML: '', + ariaLabel: strings.closeChat, + ariaExpanded: 'true', } ); const chatClose = this.createElement( @@ -651,6 +1619,10 @@ class App { chatCloseButton ); + if ( isLeft ) { + chatClose.classList.add( 'is-left' ); + } + if ( window.hyveClient.colors?.icon_background ) { chatClose.classList.add( 'is-dark' ); } @@ -667,15 +1639,57 @@ class App { id: 'hyve-window', } ); + if ( isLeft ) { + chatWindow.classList.add( 'is-left' ); + } + if ( window.hyveClient.colors?.chat_background ) { chatWindow.classList.add( 'is-dark' ); } - // Create header with menu + // Create header with assistant info and menu const chatHeader = this.createElement( 'div', { className: 'hyve-header', } ); + const headerAvatar = this.createElement( 'div', { + className: 'hyve-avatar', + } ); + + if ( 'media' === chatIcon?.type && chatIcon?.url ) { + headerAvatar.appendChild( + this.createElement( 'img', { + className: 'hyve-avatar-img', + src: chatIcon.url, + alt: '', + } ) + ); + } else { + headerAvatar.innerHTML = ROBOT_AVATAR_SVG; + } + + const headerTitle = window.hyveClient.chatName?.trim() + ? window.hyveClient.chatName.trim() + : strings.title ?? ''; + + const headerText = this.createElement( 'div', { + className: 'hyve-header-text', + innerHTML: `${ this.sanitize( + headerTitle + ) }${ + strings.status ?? '' + }`, + } ); + + const headerInfo = this.createElement( + 'div', + { className: 'hyve-header-info' }, + headerAvatar, + headerText + ); + + chatHeader.appendChild( headerInfo ); + const menuButtonElement = this.createElement( 'button', { className: 'hyve-menu-button', id: 'hyve-menu-button', @@ -683,9 +1697,16 @@ class App { '', } ); + const clearItem = ``; + + // Per-visitor sound toggle — only offered when sound is globally enabled. + const soundItem = this.isGlobalSoundEnabled() + ? `` + : ''; + const menuDropdown = this.createElement( 'div', { className: 'hyve-menu-dropdown', - innerHTML: ``, + innerHTML: soundItem + clearItem, } ); const menuContainer = this.createElement( @@ -726,19 +1747,60 @@ class App { className: 'hyve-send-message', innerHTML: '', + ariaLabel: strings.sendMessage, } ) ); + // Adapt the send icon color to the icon background, like the launcher. + if ( window.hyveClient.colors?.icon_background ) { + chatSendButton.classList.add( 'is-dark' ); + } + + if ( + ! window.hyveClient.colors?.icon_background && + undefined !== window.hyveClient.colors?.icon_background + ) { + chatSendButton.classList.add( 'is-light' ); + } + chatWindow.appendChild( chatHeader ); + + // In the dashboard test preview, make it obvious this chat is for trying + // the assistant and isn't a live/recorded conversation. + if ( this.isPreview() && strings.previewNotice ) { + const previewNotice = this.createElement( 'div', { + className: 'hyve-preview-notice', + innerHTML: `${ this.sanitize( + strings.previewNotice + ) }`, + } ); + + chatWindow.appendChild( previewNotice ); + } + chatWindow.appendChild( chatMessageBox ); + + const privacyNotice = this.renderPrivacyNotice(); + + if ( privacyNotice ) { + chatWindow.appendChild( privacyNotice ); + } + chatWrite.appendChild( chatInputText ); chatInputBox.appendChild( chatWrite ); chatInputBox.appendChild( chatSendButton ); chatWindow.appendChild( chatInputBox ); + // An explicit inline placement (block or shortcode) always wins over the + // floating auto-display, so the chat renders where it was added regardless + // of the global visibility rules. + const inlineChat = document.querySelector( '#hyve-inline-chat' ); const chatExists = document.querySelectorAll( '#hyve-chat' ); - if ( + if ( inlineChat ) { + inlineChat.appendChild( chatWindow ); + this.isInline = true; + } else if ( true === Boolean( window?.hyveClient?.isEnabled ) || 0 < chatExists.length ) { @@ -749,27 +1811,6 @@ class App { document.body.appendChild( chatWindow ); document.body.appendChild( chatOpen ); document.body.appendChild( chatClose ); - - return; - } - - const inlineChat = document.querySelector( '#hyve-inline-chat' ); - - if ( inlineChat ) { - inlineChat.appendChild( chatWindow ); - } - } - - async fetchSVG( url ) { - let svgBody = ''; - try { - const response = await fetch( url ); - if ( 200 === response.status ) { - svgBody = await response.text(); - } - } catch ( e ) { - } finally { - return svgBody; } } @@ -798,9 +1839,202 @@ class App { `, } ); + + // Match the bot message contrast so the dots stay visible on a dark + // assistant background. + if ( window.hyveClient.colors?.assistant_background ) { + preloaderDiv.classList.add( 'is-dark' ); + } + chatMessageBox.appendChild( preloaderDiv ); chatMessageBox.scrollTop = chatMessageBox.scrollHeight; } + + /** + * Live-update the widget's appearance from the admin Appearance panel, + * without tearing down the current conversation. Only used in preview mode. + * + * @param {Object} config Appearance values. + * @param {string} [config.chatName] Header title. + * @param {Object} [config.colors] Hex color values keyed by slug. + * @param {Object} [config.colorsDark] is-dark booleans keyed by slug. + * @param {Object} [config.chatIcon] Icon descriptor: { type, value, url }. + * @param {string} [config.chatPosition] Screen side: 'left' or 'right'. + * @param {boolean} [config.showTimestamp] Whether message timestamps show. + * + * @return {void} + */ + applyPreviewAppearance( config = {} ) { + const { + chatName, + colors, + colorsDark, + chatIcon, + chatPosition, + showTimestamp, + } = config; + + // Hex values drive the CSS custom properties the stylesheet reads. + if ( colors ) { + Object.entries( colors ).forEach( ( [ key, value ] ) => { + if ( value ) { + document.body.style.setProperty( `--${ key }`, value ); + } + } ); + } + + // Booleans drive the is-dark/is-light contrast classes. + if ( colorsDark ) { + window.hyveClient.colors = { ...colorsDark }; + this.refreshColorClasses(); + } + + if ( undefined !== chatName ) { + window.hyveClient.chatName = chatName; + const title = document.querySelector( '#hyve-window .hyve-title' ); + if ( title ) { + title.textContent = chatName.trim() + ? chatName.trim() + : strings.title ?? ''; + } + } + + if ( chatIcon ) { + window.hyveClient.chatIcon = chatIcon; + this.refreshIcons(); + } + + if ( undefined !== chatPosition ) { + window.hyveClient.chatPosition = chatPosition; + const isLeft = 'left' === chatPosition; + [ 'hyve-window', 'hyve-open', 'hyve-close' ].forEach( ( id ) => { + document + .getElementById( id ) + ?.classList.toggle( 'is-left', isLeft ); + } ); + } + + if ( undefined !== showTimestamp ) { + window.hyveClient.showTimestamp = showTimestamp; + // Toggle any already-rendered timestamps; new messages read the flag. + document + .querySelectorAll( '#hyve-window time' ) + .forEach( ( element ) => { + element.style.display = showTimestamp ? '' : 'none'; + } ); + } + } + + /** + * Re-apply the is-dark/is-light contrast classes to the rendered widget + * from the cached color booleans. Mirrors the logic in renderUI/addMessage. + * + * @return {void} + */ + refreshColorClasses() { + const colors = window.hyveClient.colors || {}; + + const setClasses = ( element, isDark, useLight ) => { + if ( ! element ) { + return; + } + element.classList.toggle( 'is-dark', Boolean( isDark ) ); + if ( useLight ) { + element.classList.toggle( 'is-light', ! isDark ); + } + }; + + setClasses( + document.getElementById( 'hyve-window' ), + colors.chat_background, + false + ); + + document + .querySelectorAll( '.hyve-bot-message' ) + .forEach( ( element ) => + setClasses( element, colors.assistant_background, false ) + ); + + document + .querySelectorAll( '.hyve-user-message, .hyve-suggestions' ) + .forEach( ( element ) => + setClasses( element, colors.user_background, true ) + ); + + setClasses( + document.getElementById( 'hyve-open' ), + colors.icon_background, + true + ); + setClasses( + document.getElementById( 'hyve-close' ), + colors.icon_background, + true + ); + setClasses( + document.getElementById( 'hyve-send-button' ), + colors.icon_background, + true + ); + } + + /** + * Rebuild the launcher icon and header avatar from the current chat icon. + * Mirrors the icon precedence used in renderUI. + * + * @return {void} + */ + refreshIcons() { + const chatIcon = window.hyveClient?.chatIcon || {}; + const isMedia = 'media' === chatIcon.type && chatIcon.url; + + const renderInto = ( element, mediaClass, fallback ) => { + if ( ! element ) { + return; + } + + if ( isMedia ) { + element.innerHTML = ''; + element.appendChild( + this.createElement( 'img', { + className: mediaClass, + src: chatIcon.url, + alt: '', + } ) + ); + return; + } + + element.innerHTML = fallback(); + }; + + // Launcher button: custom image, selected built-in SVG, or the default. + renderInto( + document.querySelector( '#hyve-open .collapsible.open' ), + 'hyve-icon-img', + () => { + if ( + 'svg' === chatIcon.type && + window.hyveClient?.icons?.[ chatIcon.value ] + ) { + return window.hyveClient.icons[ chatIcon.value ]; + } + + return ( + window.hyveClient?.icons?.[ 'chat-bubble-left-ellipsis' ] || + '💬' + ); + } + ); + + // Header avatar: custom image or the default robot. + renderInto( + document.querySelector( '#hyve-window .hyve-avatar' ), + 'hyve-avatar-img', + () => ROBOT_AVATAR_SVG + ); + } } export default App; diff --git a/src/frontend/style.scss b/src/frontend/style.scss index b4b24cdf..0aa19a23 100644 --- a/src/frontend/style.scss +++ b/src/frontend/style.scss @@ -1,59 +1,111 @@ +/** + * Hyve chat widget — frontend styles. + * + * Theming is driven by CSS custom properties set on by the plugin: + * --icon_background, --chat_background, --user_background, --assistant_background + */ + +$hyve-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, + Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +$hyve-radius: 16px; +$hyve-bubble-radius: 18px; +$hyve-shadow: 0 12px 32px rgba(15, 23, 42, 0.16), 0 2px 8px rgba(15, 23, 42, 0.08); +$hyve-z: 999999; + +// Launcher (floating open / close buttons). .hyve-bar-open, .hyve-bar-close { text-align: center; position: fixed; - bottom: 40px; - right: 50px; - z-index: 9999999; - - &.is-dark { - .close { - svg { - fill: #fff; - } - } - .open { - svg { - stroke: #fff; - } - } - } - - &.is-light { - .close { - svg { - fill: #000; - } - } - .open { - svg { - stroke: #000; - } - } + bottom: 24px; + right: 24px; + z-index: $hyve-z; + + // Left-anchored variant. + &.is-left { + right: auto; + left: 24px; } .open, .close { + display: flex; + align-items: center; + justify-content: center; background-color: var( --icon_background, #1155cc ); - font-size: 24px; - width: 70px; + font-size: 26px; + line-height: 1; + width: 60px; + height: 60px; + padding: 0; cursor: pointer; - height: 70px; - padding: 15px; border-radius: 50%; - border-style: none; - vertical-align: middle; - box-shadow: rgb(0 0 0 / 10%) 0px 1px 6px, rgb(0 0 0 / 20%) 0px 2px 24px; - } + border: none; + box-shadow: 0 6px 18px rgba(15, 23, 42, 0.22); + transition: transform 0.2s ease, box-shadow 0.2s ease; + -webkit-appearance: none; + appearance: none; + + &:hover { + transform: translateY(-2px) scale(1.04); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.28); + } - .close { - display: flex; - justify-content: center; - align-items: center; + &:active { + transform: scale(0.96); + } + + &:focus-visible { + outline: 3px solid rgba(17, 85, 204, 0.4); + outline-offset: 2px; + } + // The chat-bubble icons are outline (stroke) icons — color the stroke, + // not the fill, or the shape floods solid and the inner details vanish. + // stroke-width is set here because some bundled SVGs use the JSX-cased + // `strokeWidth` attribute, which raw inline SVG ignores. svg { + width: 28px; + height: 28px; + fill: none; + stroke: #fff; + stroke-width: 1.5; + } + + .hyve-icon-img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; + } + } + + .close svg { + fill: #fff; + stroke: none; + } + + // Color-scheme overrides for the icon, keyed off the chosen icon background. + &.is-dark { + .close svg { fill: #fff; } + + .open svg { + fill: none; + stroke: #fff; + } + } + + &.is-light { + .close svg { + fill: #1f2933; + } + + .open svg { + fill: none; + stroke: #1f2933; + } } } @@ -61,397 +113,635 @@ display: none; } +// In the dashboard live preview (wp-admin only), keep the launcher below the +// admin bar and menu so it doesn't float over the admin chrome. +body.wp-admin { + .hyve-bar-open, + .hyve-bar-close { + z-index: 9; + } +} +// Inline (shortcode/block) placement: render in-flow, no floating/animation. #hyve-inline-chat { .hyve-window { - display: block; + display: flex; position: relative; bottom: 0; right: 0; + width: 100%; + max-width: 100%; + height: 600px; + max-height: none; + opacity: 1; + transform: none; box-shadow: none; - border: 1px solid lightgray; - width: auto; + border: 1px solid #e5e7eb; z-index: 0; } } +// Chat window. .hyve-window { display: none; + flex-direction: column; box-sizing: border-box; - width: 350px; - height: 480px; - border-radius: 10px; + width: 384px; + height: min(620px, calc(100vh - 110px)); + border-radius: $hyve-radius; background-color: var( --chat_background, #ffffff ); - padding: 16px; - padding-top: 5px; - z-index: 9999999; + overflow: hidden; + z-index: $hyve-z; position: fixed; - bottom: 120px; - right: 54px; - box-shadow: rgb(0 0 0 / 10%) 0px 1px 6px, rgb(0 0 0 / 20%) 0px 2px 24px; + bottom: 100px; + right: 24px; + box-shadow: $hyve-shadow; + font-family: $hyve-font; + -webkit-font-smoothing: antialiased; + + // Left-anchored variant. + &.is-left { + right: auto; + left: 24px; + } + + // Entrance animation (floating mode only — toggled via `.is-open`). + opacity: 0; + transform: translateY(16px) scale(0.98); + transform-origin: bottom right; + transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.16, 1, 0.3, 1); + + &.is-open { + opacity: 1; + transform: translateY(0) scale(1); + } + + * { + box-sizing: border-box; + } + // ---- Header ---- .hyve-header { - position: relative; - padding: 0 0 10px 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-shrink: 0; + padding: 14px 16px; + border-bottom: 1px solid rgba(15, 23, 42, 0.08); - .hyve-menu-container { - position: relative; + .hyve-header-info { display: flex; - justify-content: flex-end; + align-items: center; + gap: 10px; + min-width: 0; + } + + .hyve-avatar { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 38px; + height: 38px; + border-radius: 50%; + background-color: var( --icon_background, #1155cc ); + color: #fff; + overflow: hidden; - #hyve-menu-button { - margin-right: -13px; + svg { + width: 22px; + height: 22px; } + .hyve-avatar-img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .hyve-header-text { + display: flex; + flex-direction: column; + min-width: 0; + line-height: 1.3; + } + + .hyve-title { + font-size: 15px; + font-weight: 600; + color: #111827; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .hyve-status { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #6b7280; + } + + .hyve-status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #22c55e; + box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18); + } + + .hyve-menu-container { + position: relative; + display: flex; + flex-shrink: 0; + .hyve-menu-button { background: none; border: none; cursor: pointer; - border-radius: 4px; + border-radius: 8px; + width: 34px; + height: 34px; display: flex; align-items: center; justify-content: center; transition: background-color 0.2s ease; + padding: 0; &:hover { - background-color: rgba(0, 0, 0, 0.05); + background-color: rgba(15, 23, 42, 0.06); } .hyve-menu-icon { - width: 20px; - height: 20px; - stroke: #666; + width: 22px; + height: 22px; + stroke: #6b7280; } } .hyve-menu-dropdown { position: absolute; - top: 100%; + top: calc(100% + 6px); right: 0; - background: white; - border: 1px solid #e0e0e0; - border-radius: 6px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + background: var( --chat_background, #ffffff ); + border: 1px solid rgba(15, 23, 42, 0.12); + border-radius: 12px; + box-shadow: 0 10px 28px rgba(15, 23, 42, 0.16); display: none; - z-index: 1000; - min-width: 180px; - margin-top: 4px; + z-index: 10; + min-width: 190px; + padding: 6px; + overflow: hidden; .hyve-menu-item { width: 100%; - padding: 12px 16px; + padding: 10px 12px; border: none; background: none; text-align: left; cursor: pointer; font-size: 14px; + font-family: inherit; + border-radius: 8px; display: flex; align-items: center; - gap: 8px; - color: #333; - transition: background-color 0.2s ease; + gap: 10px; + color: #374151; + transition: background-color 0.15s ease; &:hover { - background-color: #f5f5f5; + background-color: rgba(15, 23, 42, 0.06); } - &:first-child { - border-radius: 6px 6px 0 0; - } - - &:last-child { - border-radius: 0 0 6px 6px; - } - - &:only-child { - border-radius: 6px; + svg { + flex-shrink: 0; + stroke: currentColor; + opacity: 0.7; } } } } } - &.is-dark { - .hyve-header { - border-bottom-color: #444; - - .hyve-menu-container { - .hyve-menu-button { - &:hover { - background-color: rgba(255, 255, 255, 0.1); - } - - .hyve-menu-icon { - stroke: #e5e5e5; - } - } - - .hyve-menu-dropdown { - background: #2a2a2a; - border-color: #444; + // ---- Footer / input ---- + .hyve-credits { + flex-shrink: 0; + text-align: center; + font-size: 11px; + color: #9ca3af; + line-height: 1; + padding: 0 16px 4px; - .hyve-menu-item { - color: #e5e5e5; + a { + text-decoration: none; + color: #9ca3af; - &:hover { - background-color: rgba(255, 255, 255, 0.1); - } - } - } + &:hover { + color: #6b7280; } } + } - .hyve-credits { - color: #e5e5e5; - - a { - color: #e5e5e5; - } - } + .hyve-privacy-notice { + display: flex; + align-items: center; + gap: 9px; + flex-shrink: 0; + margin: 0 16px 8px; + padding: 9px 8px 9px 11px; + font-size: 12px; + line-height: 1.4; + color: #64748b; + background-color: rgba(15, 23, 42, 0.035); + border-radius: 10px; + + .hyve-privacy-notice__icon { + display: inline-flex; + flex-shrink: 0; + width: 15px; + height: 15px; + opacity: 0.75; - .hyve-message-box { - .hyve-bot-message, - .hyve-user-message { - time { - color: #e5e5e5; - } + svg { + width: 15px; + height: 15px; + stroke: currentColor; } } - .hyve-input-box { - .hyve-write { - input[type="text"] { - color: #ffffff; - - &::placeholder { - color: #e5e5e5; - } - } - } - - .hyve-send-button { - button { - &:hover, - &:focus { - svg { - fill: #e5e5e5; - } - } + .hyve-privacy-notice__text { + flex-grow: 1; + } - &:disabled { - fill: #ffffff; - } + .hyve-privacy-notice__link { + color: inherit; + font-weight: 600; + text-decoration: underline; - svg { - fill: #ffffff; - } - } + &:hover, + &:focus-visible { + color: #1155cc; } } - .hyve-message-box { - .hyve-suggestions { - span { - color: #ffffff; - } - } - } - } + .hyve-privacy-notice__dismiss { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 22px; + height: 22px; + padding: 0; + border: none; + border-radius: 6px; + background: transparent; + color: inherit; + opacity: 0.55; + cursor: pointer; + transition: opacity 0.15s ease, background-color 0.15s ease; - &.is-light { - .hyve-message-box { - .hyve-suggestions { - span { - color: #202020; - } + svg { + fill: currentColor; } - } - } - - .hyve-credits { - text-align: center; - font-size: 10px; - color: #202020; - line-height: 1; - a { - text-decoration: none; - color: #202020; + &:hover, + &:focus-visible { + opacity: 1; + background-color: rgba(15, 23, 42, 0.06); + } } } - .hyve-input-box { display: flex; - justify-content: space-between; - gap: 1rem; - position: absolute; - font-size: 12px; - bottom: 0; - left: 0; - right: 0; - padding: 18px 30px; - border-top: 1px solid lightgray; + align-items: center; + flex-shrink: 0; + gap: 8px; + padding: 12px 16px 16px; .hyve-write { - float: left; - flex-grow: 100; + flex-grow: 1; + display: flex; + align-items: center; + background-color: rgba(15, 23, 42, 0.05); + border: 1px solid transparent; + border-radius: 999px; + padding: 0 16px; + height: 44px; + transition: border-color 0.2s ease, background-color 0.2s ease; + + &:focus-within { + background-color: var( --chat_background, #ffffff ); + border-color: var( --icon_background, #1155cc ); + } input[type="text"] { - background-color: var( --chat_background, #ffffff ); - color: #202020; + background: transparent; + color: #111827; border: none; outline: none; box-shadow: none; font-size: 14px; + font-family: inherit; padding: 0; - height: 24px; - color: #202020; + margin: 0; + height: 100%; width: 100%; &::placeholder { - color: #808080; + color: #9ca3af; } } } .hyve-send-button { - float: right; - border: none; - outline: none; + flex-shrink: 0; button { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; border: none; - background-color: transparent; + border-radius: 50%; + background-color: var( --icon_background, #1155cc ); cursor: pointer; outline: none; padding: 0; + transition: transform 0.15s ease, opacity 0.15s ease, filter 0.15s ease; - &:hover, - &:focus { - svg { - fill: #5B5B66; - } + &:hover:not(:disabled) { + filter: brightness(1.08); + transform: scale(1.05); + } + + &:active:not(:disabled) { + transform: scale(0.94); } &:disabled { - fill: #808080; + opacity: 0.45; cursor: default; } svg { - fill: #808080; - width: 24px; - height: 24px; + fill: #fff; + width: 18px; + height: 18px; + margin-left: 2px; + } + } + + // Match the icon contrast to the button background, like the launcher. + &.is-light button svg { + fill: #1f2933; + } + } + } + + // ---- Preview / test-mode notice ---- + .hyve-preview-notice { + display: flex; + align-items: flex-start; + gap: 8px; + flex-shrink: 0; + padding: 9px 16px; + font-size: 12px; + line-height: 1.4; + color: #92400e; + background-color: #fef3c7; + border-bottom: 1px solid rgba(146, 64, 14, 0.15); + + svg { + flex-shrink: 0; + margin-top: 1px; + } + } + + // ---- Dark color-scheme (dark chat background) ---- + &.is-dark { + .hyve-preview-notice { + color: #fde68a; + background-color: rgba(251, 191, 36, 0.12); + border-bottom-color: rgba(251, 191, 36, 0.2); + } + + .hyve-header { + border-bottom-color: rgba(255, 255, 255, 0.1); + + .hyve-title { + color: #f9fafb; + } + + .hyve-status { + color: #cbd5e1; + } + + .hyve-menu-container { + .hyve-menu-button { + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + .hyve-menu-icon { + stroke: #cbd5e1; + } + } + + .hyve-menu-dropdown { + border-color: rgba(255, 255, 255, 0.12); + + .hyve-menu-item { + color: #e5e7eb; + + &:hover { + background-color: rgba(255, 255, 255, 0.08); + } + } } } } + + .hyve-credits, + .hyve-credits a { + color: #94a3b8; + } + + .hyve-privacy-notice { + color: #cbd5e1; + background-color: rgba(255, 255, 255, 0.06); + + .hyve-privacy-notice__link:hover, + .hyve-privacy-notice__link:focus-visible { + color: #fff; + } + + .hyve-privacy-notice__dismiss:hover, + .hyve-privacy-notice__dismiss:focus-visible { + background-color: rgba(255, 255, 255, 0.1); + } + } + + .hyve-input-box { + .hyve-write { + background-color: rgba(255, 255, 255, 0.08); + + input[type="text"] { + color: #f9fafb; + + &::placeholder { + color: #94a3b8; + } + } + } + } + + .hyve-message-box { + .hyve-bot-message time, + .hyve-user-message time { + color: #94a3b8; + } + + .hyve-suggestions span { + color: #e5e7eb; + } + } } } +// ---- Message list ---- .hyve-message-box { - height: 350px; + flex: 1 1 auto; width: 100%; - padding-right: 5px; - overflow: auto; + min-height: 0; + padding: 18px 16px 8px; + overflow-y: auto; + overflow-x: hidden; display: flex; flex-direction: column; - position: relative; + gap: 12px; + scroll-behavior: smooth; + + // Slim, unobtrusive scrollbar. + scrollbar-width: thin; + scrollbar-color: rgba(15, 23, 42, 0.2) transparent; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(15, 23, 42, 0.18); + border-radius: 999px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } .hyve-user-message, .hyve-bot-message { display: flex; flex-direction: column; - - div { - font-size: 13px; - padding: 10px; - border-radius: 5px; - width: 100%; + max-width: 82%; + word-break: break-word; + animation: hyve-message-in 0.28s cubic-bezier(0.16, 1, 0.3, 1); + + > div { + font-size: 14px; + line-height: 1.55; + padding: 11px 14px; + border-radius: $hyve-bubble-radius; + width: fit-content; + max-width: 100%; overflow-wrap: break-word; margin: 0; } p { - font-size: 13px; + font-size: 14px; + line-height: 1.55; } h1, h2, h3, h4, h5, h6 { - font-size: 13px; - margin-bottom: 12px; + font-size: 14px; + margin-bottom: 10px; } ol, ul { - padding-left: 12px; + padding-left: 18px; + margin: 6px 0; + } + + a { + text-decoration: underline; } time { font-size: 10px; - padding: 4px; - color: #000; + padding: 5px 4px 0; + color: #9ca3af; } } .hyve-user-message { margin-left: auto; - max-width: 75%; - min-width: 50%; - word-break: break-word; - margin-top: 14px; - margin-bottom: 14px; - color: white; align-items: flex-end; - - &.is-dark { - color: #ffffff; - } + color: #fff; &.is-light { - color: #000000; + color: #111827; } - div { + > div { background-color: var( --user_background, #1155cc ); - justify-content: flex-end; + border-bottom-right-radius: 6px; + } + + time { + text-align: right; } } .hyve-bot-message { margin-right: auto; - color: #000; - max-width: 75%; - min-width: 50%; - word-break: break-word; - margin-top: 14px; - margin-bottom: 14px; - border-radius: 5px; align-items: flex-start; + color: #111827; &.is-dark { - color: #ffffff; + color: #f9fafb; } - div { - background-color: var( --assistant_background, #ecf1fb ); - justify-content: flex-start; + > div { + background-color: var( --assistant_background, #eef2ff ); + border-bottom-left-radius: 6px; p { - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; + + & + p { + margin-top: 8px; + } } pre { - background-color: #282c34; - color: #abb2bf; - padding: 1em; - border-radius: 0; - font-family: monospace; + background-color: #1e2330; + color: #e5e7eb; + padding: 12px 14px; + border-radius: 10px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12.5px; line-height: 1.5; overflow-x: auto; white-space: pre; word-spacing: normal; - margin: 20px -10px; + margin: 12px 0; * { - color: #abb2bf; + color: #e5e7eb; word-break: normal; word-wrap: normal; } @@ -459,90 +749,108 @@ ::selection { background-color: #3e4451; } - - ::before { - content: attr(data-content); - } - - ::before:has(+ :matches(function, const, return, let, var)) { - color: #c678dd; - } } img { max-width: 100%; height: auto; - border-radius: 5px; + border-radius: 10px; cursor: pointer; + margin-top: 6px; } code { - background: #d0effb; + background: rgba(15, 23, 42, 0.08); border-radius: 5px; border: none; - padding: 0 3px; - color: #333; + padding: 1px 5px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12.5px; + color: inherit; + } + + pre code { + background: transparent; + padding: 0; } } } + // Predefined-question chips, shown after the welcome message. .hyve-suggestions { display: flex; flex-wrap: wrap; - justify-content: right; - position: absolute; - bottom: 0; - right: 0; - padding: 20px 0; - gap: 4px; - - &.is-dark { - button { - background-color: var( --user_background, #1155cc); - color: #ffffff; - } - } - - &.is-light { - button { - background-color: var( --user_background, #1155cc); - color: #202020; - } - } + gap: 8px; + margin-top: 2px; span { font-size: 12px; font-weight: 500; flex: 0 0 100%; - text-align: right; - color: #202020; + color: #6b7280; + margin-bottom: 2px; } + // Filled with the user color so the label always contrasts with its own + // chip background (an outline chip can vanish when the user and chat + // colors are similar). button { - background: #ecf1fb99; + background: var( --user_background, #1155cc ); box-shadow: none; - border: 0.5px solid rgb(145, 145, 145); - padding: 6px 8px; - border-radius: 8px; + border: 1px solid transparent; + color: #fff; + padding: 8px 14px; + border-radius: 999px; cursor: pointer; font-size: 13px; + font-family: inherit; text-transform: none; font-weight: 500; text-align: left; - line-height: 1.5; + line-height: 1.4; white-space: normal; + transition: filter 0.15s ease, transform 0.1s ease; + + &:hover { + filter: brightness(1.08); + } + + &:active { + transform: scale(0.97); + } + } + + // Light user color → dark label (and a hairline to define the chip). + &.is-light button { + color: #111827; + border-color: rgba(15, 23, 42, 0.12); } } - .hyve-preloader-dots { - display: inline-block; - color: var( --user_background, #ecf1fb ); + // Typing indicator. + .hyve-preloader { + .hyve-preloader-dots { + background-color: var( --assistant_background, #eef2ff ); + border-radius: $hyve-bubble-radius; + border-bottom-left-radius: 6px; + padding: 14px 16px; + display: inline-flex; + align-items: center; + gap: 5px; + width: fit-content; + } - span { - animation: hyve-bounce 1.4s infinite both; - display: inline-block; - font-size: 2.5em; + // Dots inherit the bot-message text color (which flips for a dark + // assistant background), so they always contrast with the bubble. + .hyve-preloader-dots span { + width: 7px; + height: 7px; + border-radius: 50%; + background-color: currentColor; + opacity: 0.5; + font-size: 0; line-height: 0; + animation: hyve-bounce 1.4s infinite both; &:nth-child(2) { animation-delay: 0.2s; @@ -564,25 +872,229 @@ } @keyframes hyve-bounce { - 0%, 80%, 100% { transform: scale(1); opacity: 0.5; } - 40% { transform: scale(1.3); opacity: 1; } + 0%, 80%, 100% { transform: translateY(0); opacity: 0.4; } + 40% { transform: translateY(-5px); opacity: 1; } +} + +@keyframes hyve-message-in { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +// Proactive teaser: a small speech bubble next to the closed launcher. +.hyve-teaser { + position: fixed; + bottom: 32px; + right: 96px; + z-index: $hyve-z; + display: flex; + align-items: flex-start; + max-width: min(300px, calc(100vw - 120px)); + font-family: $hyve-font; + animation: hyve-message-in 0.25s ease both; + + &.is-left { + right: auto; + left: 96px; + } + + .hyve-teaser__message { + background: var( --assistant_background, #fff ); + color: #111827; + border: none; + border-radius: $hyve-bubble-radius; + box-shadow: $hyve-shadow; + padding: 12px 16px; + font-size: 14px; + line-height: 1.45; + text-align: left; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + + &:hover { + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.28); + } + + &:focus-visible { + outline: 3px solid rgba(17, 85, 204, 0.4); + outline-offset: 2px; + } + } + + .hyve-teaser__dismiss { + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + margin: -6px 0 0 -8px; + padding: 0; + background: var( --assistant_background, #fff ); + color: #64748b; + border: none; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.2); + cursor: pointer; + -webkit-appearance: none; + appearance: none; + + &:hover { + color: #111827; + } + + &:focus-visible { + outline: 3px solid rgba(17, 85, 204, 0.4); + outline-offset: 2px; + } + + svg { + fill: currentColor; + } + } + + // Light text on a dark assistant background, matching .hyve-bot-message. + &.is-dark { + .hyve-teaser__message { + color: #f9fafb; + } + + .hyve-teaser__dismiss { + color: rgba(249, 250, 251, 0.75); + + &:hover { + color: #f9fafb; + } + } + } +} + +// Respect users who prefer reduced motion. +@media (prefers-reduced-motion: reduce) { + .hyve-window, + .hyve-window.is-open, + .hyve-message-box .hyve-user-message, + .hyve-message-box .hyve-bot-message, + .hyve-bar-open .open, + .hyve-bar-close .close, + .hyve-teaser { + transition: none; + animation: none; + transform: none; + } } -@media screen and (max-width: 396px) { +@media screen and (max-width: 480px) { .hyve-bar-open, .hyve-bar-close { - bottom: 20px; - right: 21px; + bottom: 18px; + right: 18px; - &.hyve-bar-close { - bottom: 21px; - right: 25px; + &.is-left { + right: auto; + left: 18px; } } - .hyve-window { - right: 14px; - bottom: 87px; - width: 80% + .hyve-teaser { + bottom: 26px; + right: 90px; + max-width: calc(100vw - 108px); + + &.is-left { + right: auto; + left: 90px; + } + } + + // Full-width on small screens regardless of the chosen side. + .hyve-window, + .hyve-window.is-left { + right: 12px; + left: 12px; + bottom: 90px; + width: auto; + height: min(70vh, calc(100vh - 110px)); + } +} + +.hyve-message-box .hyve-bot-message { + + .hyve-source { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; + } + + .hyve-source__intro { + font-size: 12.5px; + font-weight: 500; + + // Inherit the message text color (adapts to the bubble background via the + // is-dark/is-light classes) and soften it for a label. + color: inherit; + opacity: 0.6; + } + + .hyve-source__link { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + border: 1px solid rgba( 128, 128, 128, 0.3 ); + border-radius: 7px; + background: rgba( 128, 128, 128, 0.12 ); + + // The number follows the message text color so it stays legible on both + // light and dark bubble backgrounds, like the rest of the chat. + color: inherit; + opacity: 0.85; + font-size: 12px; + font-weight: 700; + font-variant-numeric: tabular-nums; + text-decoration: none; + transition: opacity 0.15s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease; + + &:hover, + &:focus-visible { + opacity: 1; + background: var( --icon_background, #1155cc ); + border-color: var( --icon_background, #1155cc ); + color: #fff; + } + } + + .hyve-source__label { + position: absolute; + bottom: calc( 100% + 6px ); + left: 0; + width: max-content; + max-width: 200px; + padding: 5px 9px; + border-radius: 7px; + background: #141824; + color: #fff; + font-size: 11.5px; + font-weight: 500; + line-height: 1.35; + white-space: normal; + opacity: 0; + visibility: hidden; + transform: translateY( 4px ); + pointer-events: none; + transition: opacity 0.15s ease, transform 0.15s ease; + z-index: 2; + } + + .hyve-source__link:hover .hyve-source__label, + .hyve-source__link:focus-visible .hyve-source__label { + opacity: 1; + visibility: visible; + transform: translateY( 0 ); } } diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index 31e3c781..00000000 --- a/tailwind.config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ './src/style.scss', './src/*.js', './src/**/*.js' ], - theme: {}, - plugins: [] -}; diff --git a/tests/e2e/specs/block.spec.js b/tests/e2e/specs/block.spec.js index 9d7b1cbd..38e7154b 100644 --- a/tests/e2e/specs/block.spec.js +++ b/tests/e2e/specs/block.spec.js @@ -24,5 +24,14 @@ test.describe( 'Block Editor', () => { .contentFrame() .getByRole( 'button', { name: 'Click here to add content.' } ) ).toBeVisible(); + + await expect( + page + .locator( 'iframe[name="editor-canvas"]' ) + .contentFrame() + .getByText( + 'Hyve Chatbot bubble will appear on this page. No further action needed.' + ) + ).toBeVisible(); } ); } ); diff --git a/tests/e2e/specs/chat.spec.js b/tests/e2e/specs/chat.spec.js index 2f31ac6c..d6effe10 100644 --- a/tests/e2e/specs/chat.spec.js +++ b/tests/e2e/specs/chat.spec.js @@ -5,12 +5,23 @@ test.describe( 'Chat', () => { /** * Initialize the Chat App. * - * This bypass the knowledge base check. + * The app auto-initializes on load when it is allowed to show; a second + * initialize would render a duplicate widget whose duplicated element ids + * leave dead event listeners. Only initialize manually when nothing + * rendered. * * @param {import('@playwright/test').Page} page */ async function initializeChatApp( page ) { await page.evaluate( () => { + if ( + document.querySelector( + '#hyve-open, #hyve-window, .hyve-input-text' + ) + ) { + return; + } + window?.hyveApp?.initialize(); } ); } @@ -36,7 +47,8 @@ test.describe( 'Chat', () => { // Open chat window await page - .getByRole( 'button', { name: '💬' } ) + .locator( '#hyve-open' ) + .getByRole( 'button' ) .click( { force: true } ); await expect( page.locator( '#hyve-window' ) ).toBeVisible(); await expect( page.locator( '#hyve-message-box' ) ).toBeVisible(); @@ -70,7 +82,8 @@ test.describe( 'Chat', () => { await mockChatResponse( page ); await page - .getByRole( 'button', { name: '💬' } ) + .locator( '#hyve-open' ) + .getByRole( 'button' ) .click( { force: true } ); await page.waitForSelector( '.hyve-bot-message' ); @@ -136,7 +149,8 @@ test.describe( 'Chat', () => { await mockChatResponse( page ); await page - .getByRole( 'button', { name: '💬' } ) + .locator( '#hyve-open' ) + .getByRole( 'button' ) .click( { force: true } ); await page.waitForSelector( '.hyve-bot-message' ); @@ -181,7 +195,8 @@ test.describe( 'Chat', () => { } ); await page - .getByRole( 'button', { name: '💬' } ) + .locator( '#hyve-open' ) + .getByRole( 'button' ) .click( { force: true } ); await page.waitForSelector( '.hyve-bot-message' ); diff --git a/tests/e2e/specs/dashboard.spec.js b/tests/e2e/specs/dashboard.spec.js index d12e0e69..47bbe9ca 100644 --- a/tests/e2e/specs/dashboard.spec.js +++ b/tests/e2e/specs/dashboard.spec.js @@ -1,481 +1,249 @@ import { test, expect } from '@wordpress/e2e-test-utils-playwright'; -import { - mockConfirmDeleteThreadResponse, - mockGetThreadsResponse, -} from '../utils'; +import { mockGetThreadsResponse, mockStatsResponse } from '../utils'; + +const HYVE_ADMIN = 'admin.php?page=hyve'; + +const CHART = { + legend: { + messagesLabel: 'User Messages per Day', + sessionsLabel: 'Active Sessions per Day', + }, + data: { + messages: [ 4, 9, 13 ], + sessions: [ 3, 6, 2 ], + }, + labels: [ 'Jul 1', 'Jul 2', 'Jul 3' ], +}; test.describe( 'Dashboard', () => { - test.beforeEach( async ( { admin } ) => { - await admin.visitAdminPage( 'admin.php?page=hyve' ); - } ); + test( 'renders the app shell: header, tabs and active state', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( HYVE_ADMIN ); - test( 'check dashboard URL', async ( { page } ) => { expect( await page.locator( '#hyve-options' ).count() ).toBe( 1 ); - } ); - - test( 'check shortcuts', async ( { page } ) => { - await page - .getByRole( 'button', { name: 'Knowledge Base → In Knowledge' } ) - .click( { force: true } ); + // Header bar contents. + const bar = page.locator( '.hyve-next__bar' ); + await expect( bar.getByText( 'Hyve', { exact: true } ) ).toBeVisible(); + await expect( bar.getByText( 'API connected' ) ).toBeVisible(); + await expect( bar.getByRole( 'link', { name: 'Docs' } ) ).toBeVisible(); await expect( - page.getByRole( 'heading', { name: 'Knowledge Base' } ) + bar.getByRole( 'link', { name: 'Upgrade to Pro' } ) ).toBeVisible(); - await page - .getByRole( 'button', { name: 'Dashboard' } ) - .click( { force: true } ); - - await page - .getByRole( 'button', { name: 'Personalize → Customize Hyve' } ) - .click( { force: true } ); - + // Tab navigation with the current page marked. + const tabs = page.getByRole( 'navigation', { name: 'Hyve sections' } ); await expect( - page.getByRole( 'heading', { name: 'General Settings' } ) - ).toBeVisible(); - } ); - - /** - * Navigate through the dashboard tabs and check for rendering issues. - */ - test( 'check tabs', async ( { page } ) => { + tabs.getByRole( 'button', { name: 'Dashboard' } ) + ).toHaveAttribute( 'aria-current', 'page' ); await expect( - page.getByText( 'Enable Chat on all the pages' ) + tabs.getByRole( 'button', { name: 'Knowledge Base' } ) ).toBeVisible(); - - await page - .getByRole( 'button', { name: 'Knowledge Base', exact: true } ) - .click( { force: true } ); - await expect( - page.getByText( 'Cosine Similarity Threshold' ) + tabs.getByRole( 'button', { name: 'Messages' } ) ).toBeVisible(); - await expect( - page.getByRole( 'slider', { name: 'Cosine Similarity Threshold' } ) + tabs.getByRole( 'button', { name: 'Settings' } ) ).toBeVisible(); + } ); - await page - .getByRole( 'button', { name: 'WordPress → Import your' } ) - .click( { force: true } ); + test( 'stat cards show the numbers fetched from the stats endpoint', async ( { + page, + admin, + } ) => { + await mockStatsResponse( page, { + threads: 42, + messages: 99, + totalChunks: 7, + chart: CHART, + } ); - await page - .getByRole( 'button', { name: 'Add Posts' } ) - .click( { force: true } ); + await admin.visitAdminPage( HYVE_ADMIN ); - await expect( page.getByLabel( 'Post Type' ) ).toBeVisible(); - await expect( - page.getByRole( 'searchbox', { name: 'Search for Posts' } ) - ).toBeVisible(); + const stats = page.locator( '.hyve-next-stats' ); + await expect( stats ).toBeVisible(); - await page - .getByRole( 'button', { name: 'Requires Update' } ) - .click( { force: true } ); + await expect( stats.getByText( '42', { exact: true } ) ).toBeVisible(); + await expect( stats.getByText( '99', { exact: true } ) ).toBeVisible(); await expect( - page.locator( 'div' ).filter( { hasText: /^Updated$/ } ) + stats.getByText( 'Sessions', { exact: true } ) ).toBeVisible(); - - await page - .getByRole( 'button', { name: 'Failed Moderation' } ) - .click( { force: true } ); await expect( - page.getByRole( 'heading', { name: 'Failed Moderation' } ) + stats.getByText( 'Messages', { exact: true } ) ).toBeVisible(); - - await page - .getByRole( 'button', { name: 'Messages' } ) - .click( { force: true } ); await expect( - page.getByRole( 'heading', { name: 'Messages' } ) + stats.getByText( 'Knowledge Base', { exact: true } ) ).toBeVisible(); - await page - .getByRole( 'button', { name: 'Integrations' } ) - .click( { force: true } ); + // Free plan: the chunk meter foot shows usage of the local limit. await expect( - page.getByRole( 'textbox', { name: 'API Key' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'textbox', { name: 'API Endpoint' } ) + stats.getByText( 'of the free limit used.' ) ).toBeVisible(); - await page - .getByRole( 'button', { name: 'Settings' } ) - .click( { force: true } ); - await expect( - page.getByRole( 'textbox', { name: 'Welcome Message' } ) - ).toBeVisible(); + // With content indexed, the visibility notice shows instead of the + // setup checklist. Assert on its action, which is the same whatever + // display mode the site is in. await expect( - page.getByRole( 'textbox', { name: 'Default Message' } ) + page.getByRole( 'button', { name: 'Manage visibility' } ) ).toBeVisible(); + await expect( page.getByText( "Let's get Hyve running" ) ).toBeHidden(); + } ); - await page - .getByRole( 'button', { name: 'Assistant' } ) - .click( { force: true } ); - await expect( - page.getByText( 'Model', { exact: true } ) - ).toBeVisible(); - await expect( - page.getByRole( 'slider', { name: 'Temperature' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'slider', { name: 'Top P' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'button', { name: 'Save' } ) - ).toBeVisible(); + test( 'usage chart renders when data exists', async ( { page, admin } ) => { + await mockStatsResponse( page, { + threads: 5, + messages: 20, + totalChunks: 7, + chart: CHART, + } ); - await page - .getByRole( 'button', { name: 'Moderation' } ) - .click( { force: true } ); - await expect( - page.getByRole( 'heading', { name: 'Moderation Settings' } ) - ).toBeVisible(); - expect( - await page.locator( '.components-range-control__slider' ).count() - ).toBeGreaterThan( 0 ); - await expect( - page.getByRole( 'button', { name: 'Save' } ) - ).toBeVisible(); + await admin.visitAdminPage( HYVE_ADMIN ); - await page - .getByRole( 'button', { name: 'Advanced' } ) - .click( { force: true } ); await expect( - page.getByRole( 'textbox', { name: 'API Key' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'button', { name: 'Save' } ) + page.getByRole( 'img', { name: 'Messages and sessions per day' } ) ).toBeVisible(); + await expect( page.getByLabel( 'Show data for' ) ).toBeVisible(); } ); - test( 'check posts list rendering on Knowledge Base > WordPress Import', async ( { + test( 'usage chart is replaced by a placeholder without data', async ( { page, + admin, } ) => { - await page.route( - /.*rest_route=%2Fhyve%2Fv1%2Fdata.*offset=0.*status=included.*/, - async ( route ) => { - await route.fulfill( { - status: 200, - contentType: 'application/json', - body: JSON.stringify( { - posts: [ - { ID: 121, title: 'Shop', content: '' }, - { - ID: 123, - title: 'Checkout', - }, - { - ID: 94, - title: 'Portofolio', - }, - { - ID: 1, - title: 'Hello world!', - }, - ], - more: false, - totalChunks: '4', - } ), - } ); - } - ); + await mockStatsResponse( page, { totalChunks: 7 } ); - await page - .getByRole( 'button', { name: 'Knowledge Base', exact: true } ) - .click( { force: true } ); + await admin.visitAdminPage( HYVE_ADMIN ); - await page - .getByRole( 'button', { name: 'WordPress → Import your' } ) - .click( { force: true } ); - - await expect( page.getByText( 'Checkout' ) ).toBeVisible(); + await expect( + page.getByText( + 'Usage data will appear here once visitors start chatting.' + ) + ).toBeVisible(); + await expect( + page.getByRole( 'img', { name: 'Messages and sessions per day' } ) + ).toBeHidden(); } ); - test( 'check message history rendering', async ( { page } ) => { - await mockGetThreadsResponse( page ); + test( 'setup checklist shows while the knowledge base is empty and links to content', async ( { + page, + admin, + } ) => { + await mockStatsResponse( page, { totalChunks: 0 } ); - await page - .getByRole( 'button', { name: 'Messages', exact: true } ) - .click( { force: true } ); + await admin.visitAdminPage( HYVE_ADMIN ); - await page - .getByRole( 'button', { name: 'How to reset my password?' } ) - .click( { force: true } ); - - // Check Thread ID. await expect( - page.getByText( 'S1sTWm3SoQFa5D0LxzNpY9mE' ) + page.getByText( "Let's get Hyve running" ) ).toBeVisible(); - // Check message rendering from user. + // The API key exists in the test environment, so step 1 counts as done. + await expect( page.getByText( '1 of 2 steps done' ) ).toBeVisible(); await expect( - page.getByText( 'I did not receive the email' ) + page.getByRole( 'button', { name: 'Add content', exact: true } ) ).toBeVisible(); - // Check message rendering from bot. + await page + .getByRole( 'button', { name: 'Add content', exact: true } ) + .click(); + await expect( - page.getByText( 'Please check your spam folder' ) + page.getByRole( 'heading', { name: 'Add a source' } ) ).toBeVisible(); } ); - test( 'check posts list rendering on Knowledge Base > Failed Moderation', async ( { + test( 'recent conversations list mocked threads and View all opens Messages', async ( { page, + admin, } ) => { - await page.route( - /.*rest_route=%2Fhyve%2Fv1%2Fdata.*offset=0.*status=moderation.*/, - async ( route ) => { - await route.fulfill( { - status: 200, - contentType: 'application/json', - body: JSON.stringify( { - posts: [ - { - ID: 121, - title: 'Shop', - content: '', - review: { - hate: 0.5, - }, - }, - { - ID: 123, - title: 'Checkout', - content: 'Test checkout content', - review: { - hate: 0.5, - }, - }, - { - ID: 94, - title: 'Portofolio', - content: 'Test portfolio content', - review: { - hate: 0.5, - }, - }, - { - ID: 1, - title: 'Hello world!', - content: 'Test hello world content', - review: { - hate: 0.5, - }, - }, - ], - more: false, - totalChunks: '4', - } ), - } ); - } - ); - - await page - .getByRole( 'button', { name: 'Knowledge Base', exact: true } ) - .click( { force: true } ); + await mockStatsResponse( page, { totalChunks: 7, chart: CHART } ); + await mockGetThreadsResponse( page ); - await page - .getByRole( 'button', { name: 'Failed Moderation' } ) - .click( { force: true } ); + await admin.visitAdminPage( HYVE_ADMIN ); - await expect( page.getByText( '123' ) ).toBeVisible(); - await expect( page.getByText( 'Checkout' ) ).toBeVisible(); await expect( - page.getByRole( 'button', { name: 'More Info' } ).nth( 1 ) - ).toBeVisible(); - await expect( - page.getByRole( 'button', { name: 'Update' } ).nth( 2 ) + page.getByText( 'How to reset my password?' ) ).toBeVisible(); - // Check Review Modal. - await page - .getByRole( 'button', { name: 'More Info' } ) - .nth( 1 ) - .click(); + await page.getByRole( 'button', { name: 'View all' } ).click(); await expect( - page.getByRole( 'heading', { name: 'Failed Moderation: Checkout' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'heading', { name: 'Hate Speech' } ) - ).toBeVisible(); - await expect( - page - .locator( 'div' ) - .filter( { hasText: /^Hate Speech50%$/ } ) - .locator( 'div' ) - .nth( 3 ) - ).toBeVisible(); - await expect( - page.getByRole( 'button', { name: 'Override Moderation' } ) + page.getByRole( 'heading', { name: 'Messages' } ) ).toBeVisible(); await expect( - page.getByRole( 'button', { name: 'Close' } ) + page.getByRole( 'heading', { name: 'Conversations' } ) ).toBeVisible(); } ); - test( 'check route change by Dashboard URL', async ( { page, admin } ) => { - await admin.visitAdminPage( 'admin.php' ); // NOTE: this negate the first redirect from `beforeEach`. - await admin.visitAdminPage( 'admin.php?page=hyve&nav=advanced' ); + test( 'get started shortcuts navigate to their screens', async ( { + page, + admin, + } ) => { + await mockStatsResponse( page, { totalChunks: 7, chart: CHART } ); - await expect( - page.getByRole( 'textbox', { name: 'API Key' } ) - ).toBeVisible(); + await admin.visitAdminPage( HYVE_ADMIN ); - // Check if the navigation is not blocked. await page - .getByRole( 'button', { name: 'Assistant' } ) - .click( { force: true } ); + .getByRole( 'button', { name: 'Grow the Knowledge Base' } ) + .click(); await expect( - page.getByRole( 'heading', { name: 'Assistant Settings' } ) + page.getByRole( 'heading', { name: 'Add a source' } ) ).toBeVisible(); - } ); - test( 'choose assistant model', async ( { page } ) => { - await page - .getByRole( 'button', { name: 'Settings' } ) - .click( { force: true } ); - await page - .getByRole( 'button', { name: 'Assistant' } ) - .click( { force: true } ); + await page.goBack(); await page - .getByRole( 'radio', { name: 'GPT-4.1 nano' } ) - .click( { force: true } ); + .getByRole( 'button', { name: 'Personalize the chat' } ) + .click(); await expect( - page.getByRole( 'radio', { name: 'GPT-4.1 nano' } ) - ).toBeChecked(); + page.getByText( 'Where should Hyve appear?' ).first() + ).toBeVisible(); } ); - test( 'delete conversation/thread', async ( { page } ) => { - await mockGetThreadsResponse( page ); - - await page - .getByRole( 'button', { name: 'Messages', exact: true } ) - .click( { force: true } ); - await page - .getByRole( 'button', { name: 'How to reset my password?' } ) - .click( { force: true } ); + test( 'deep links open the right panel and browser back works', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=ai-advanced` + ); - await mockConfirmDeleteThreadResponse( page ); - await page - .getByRole( 'button', { name: 'Delete conversation' } ) - .click( { force: true } ); await expect( - page.getByRole( 'button', { name: 'How to reset my password?' } ) - ).toBeHidden(); - await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); - } ); - - test( 'check chart rendering', async ( { page } ) => { - await page.evaluate( () => { - window.hyve = window.hyve || {}; - window.hyve.chart = { - legend: { - messagesLabel: 'User messages', - sessionsLabel: 'Sessions', - }, - data: { - messages: [ 0, 0, 0 ], - sessions: [ 1, 2, 3 ], - }, - labels: [ 'Mar 20', 'Mar 21', 'Mar 22' ], - }; - } ); - - await page - .getByRole( 'button', { name: 'Knowledge Base', exact: true } ) - .click( { force: true } ); - await page - .getByRole( 'button', { name: 'Dashboard' } ) - .click( { force: true } ); - await page.locator( '#messages-chart' ).scrollIntoViewIfNeeded(); - await page.locator( '#sessions-chart' ).scrollIntoViewIfNeeded(); - - await expect( page.locator( '#messages-chart' ) ).toBeVisible(); - await expect( page.locator( '#sessions-chart' ) ).toBeVisible(); - await expect( page.getByText( 'Show data for' ) ).toBeVisible(); - await expect( page.getByLabel( 'Show data for' ) ).toBeVisible(); - } ); - - test( 'check chart hiding when the data is empty', async ( { page } ) => { - await page.evaluate( () => { - window.hyve = window.hyve || {}; - window.hyve.chart = { - legend: { - messagesLabel: 'User messages', - sessionsLabel: 'Sessions', - }, - data: { - messages: [], - sessions: [], - }, - labels: [], - }; - } ); - - await page - .getByRole( 'button', { name: 'Knowledge Base', exact: true } ) - .click( { force: true } ); - await page - .getByRole( 'button', { name: 'Dashboard' } ) - .click( { force: true } ); - - await expect( page.locator( '#messages-chart' ) ).toBeHidden(); - await expect( page.locator( '#sessions-chart' ) ).toBeHidden(); - await expect( page.getByText( 'Show data for' ) ).toBeHidden(); - await expect( page.getByLabel( 'Show data for' ) ).toBeHidden(); - } ); + page.getByRole( 'heading', { name: 'Advanced tuning' } ) + ).toBeVisible(); - test( 'empty knowledge base warning', async ( { page } ) => { + await page.getByRole( 'button', { name: 'Provider & model' } ).click(); await expect( - page.getByText( 'Your Knowledge Base is' ) + page.getByRole( 'heading', { name: 'OpenAI' } ) ).toBeVisible(); - await page - .getByRole( 'button', { name: 'Click here to add content.' } ) - .click( { force: true } ); + await page.goBack(); await expect( - page.getByRole( 'button', { name: 'WordPress → Import your' } ) + page.getByRole( 'heading', { name: 'Advanced tuning' } ) ).toBeVisible(); } ); - test( 'check service error rendering', async ( { page, admin } ) => { + test( 'service errors render reactively from the store', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( HYVE_ADMIN ); + await page.evaluate( () => { - window.hyve = window.hyve || {}; - window.hyve.serviceErrors = [ + window.wp.data.dispatch( 'hyve' ).setServiceErrors( [ { code: 'invalid_api_key', - message: - 'Incorrect API key provided: sk-IxcjM*******************************************test. You can find your API key at https://platform.openai.com/account/api-keys.', - date: '2025-06-05T15:01:01+00:00', + message: 'Incorrect API key provided.', + date: '2026-07-10T15:01:01+00:00', provider: 'OpenAI', }, - { - code: 403, - message: - 'Invalid credentials. Please check your API key and endpoint URL.', - date: '2025-06-05T14:57:31+00:00', - provider: 'Qdrant', - }, - ]; + ] ); } ); - // Trigger the rendering via React tree refresh. - await page - .getByRole( 'button', { name: 'Integrations' } ) - .click( { force: true } ); - - // Check that service errors are displayed. await expect( page.getByText( - '[OpenAI] Service Error: Incorrect API key provided' + '[OpenAI] Service Error: Incorrect API key provided.' ) ).toBeVisible(); - await expect( - page - .locator( '#hyve-options' ) - .getByText( '[Qdrant] Service Error: Invalid credentials.' ) - ).toBeVisible(); } ); } ); diff --git a/tests/e2e/specs/knowledge-base.spec.js b/tests/e2e/specs/knowledge-base.spec.js new file mode 100644 index 00000000..601c3c35 --- /dev/null +++ b/tests/e2e/specs/knowledge-base.spec.js @@ -0,0 +1,595 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; +import { HYVE_DATA_API_ROUTE_PATTERN, isDataCountsRequest } from '../utils'; + +const HYVE_ADMIN = 'admin.php?page=hyve'; + +/** + * Posts returned by the mocked WordPress drill listing, one per visibility + * type. Order matters: the per-row "Add" buttons are matched by index below. + */ +const MOCK_POSTS = [ + { ID: 201, title: 'Pickleball Rules', type: 'post', visibility: 'public' }, + { + ID: 202, + title: 'Internal Refund Policy', + type: 'post', + visibility: 'private', + }, + { + ID: 203, + title: 'Partner Discount Codes', + type: 'post', + visibility: 'password', + }, +]; + +/** + * Build indexed-content rows for the unified listing mock. + * + * @param {number} count How many rows to build. + * @param {number} offset Starting index for IDs and titles. + * @return {Object[]} Rows shaped like the `/data?status=included` response. + */ +const indexedRows = ( count, offset = 0 ) => + Array.from( { length: count }, ( _, index ) => ( { + ID: 1000 + offset + index, + title: `Indexed Post ${ offset + index + 1 }`, + type: 'post', + chunks: 1, + } ) ); + +/** + * Mock every `/data` request the Knowledge Base screen makes. + * + * @param {import("@playwright/test").Page} page The page. + * @param {Object} handlers Response overrides keyed by request kind. + */ +async function mockDataApi( page, handlers = {} ) { + await page.route( HYVE_DATA_API_ROUTE_PATTERN, async ( route ) => { + const request = route.request(); + const url = request.url(); + + if ( isDataCountsRequest( url ) ) { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( + handlers.counts ?? { pending: 0, moderation: 0 } + ), + } ); + return; + } + + // apiFetch tunnels DELETE as POST with a method-override header. + const method = + request.headers()[ 'x-http-method-override' ] ?? request.method(); + + if ( 'DELETE' === method ) { + if ( handlers.onDelete ) { + handlers.onDelete( url ); + } + + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( true ), + } ); + return; + } + + if ( 'POST' === method ) { + if ( handlers.onAdd ) { + handlers.onAdd( request.postDataJSON() ); + } + + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( true ), + } ); + return; + } + + if ( url.includes( 'status=included' ) && handlers.included ) { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( handlers.included( url ) ), + } ); + return; + } + + if ( url.includes( 'status=pending' ) && handlers.pending ) { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( handlers.pending ), + } ); + return; + } + + if ( url.includes( 'status=moderation' ) && handlers.moderation ) { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( handlers.moderation ), + } ); + return; + } + + // The WordPress drill listing carries no status filter. + if ( ! url.includes( 'status=' ) && handlers.listing ) { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( handlers.listing ), + } ); + return; + } + + await route.continue(); + } ); +} + +const openWordPressDrill = async ( page, admin ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb&sub=source-wordpress` ); + + // Wait for the (debounced) listing to render. + await expect( page.getByText( 'Pickleball Rules' ) ).toBeVisible(); +}; + +test.describe( 'Knowledge Base', () => { + test( 'all sources grid shows WordPress and pro-locked cards', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb` ); + + const grid = page.locator( '.hyve-next-src' ); + + await expect( + grid.getByRole( 'button', { name: /WordPress/ } ) + ).toBeVisible(); + await expect( + grid.getByRole( 'button', { name: /Custom Data/ } ) + ).toBeVisible(); + await expect( + grid.getByRole( 'button', { name: /Website URL/ } ) + ).toBeVisible(); + await expect( + grid.getByRole( 'button', { name: /Sitemap/ } ) + ).toBeVisible(); + await expect( + grid.getByRole( 'button', { name: /Documents/ } ) + ).toBeVisible(); + + // Every pro source is badged on the free plan. + await expect( grid.getByText( 'Pro', { exact: true } ) ).toHaveCount( + 4 + ); + + // FAQ is a Pro-badged subnav entry. + await expect( + page.getByRole( 'button', { name: 'FAQ' } ) + ).toBeVisible(); + } ); + + test( 'locked sources show a dummy preview and an upsell', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb` ); + + await page.getByRole( 'button', { name: /Custom Data/ } ).click(); + + await expect( + page.getByText( 'Custom Data is a Premium feature' ) + ).toBeVisible(); + await expect( + page.getByText( 'Halloween Limited Time Deal Information' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Unlock with Pro' } ) + ).toBeVisible(); + + // The back link returns to the sources grid. + await page.getByRole( 'button', { name: '← All sources' } ).click(); + await expect( + page.getByRole( 'heading', { name: 'Add a source' } ) + ).toBeVisible(); + + // Spot-check a second source for its own copy. + await page.getByRole( 'button', { name: /Sitemap/ } ).click(); + await expect( + page.getByText( 'Sitemap Crawling is a Premium feature' ) + ).toBeVisible(); + } ); + + test( 'locked FAQ shows the preview questions and upsell', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb&sub=faq` ); + + await expect( + page.getByText( 'FAQ is a Premium feature' ) + ).toBeVisible(); + await expect( + page.getByText( 'How do I reset my password?' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Unlock with Pro' } ) + ).toBeVisible(); + } ); + + test( 'indexed content lists rows with count, chunks chip and pagination', async ( { + page, + admin, + } ) => { + await mockDataApi( page, { + included: ( url ) => + url.includes( 'offset=20' ) + ? { + posts: indexedRows( 5, 20 ), + more: false, + total: 25, + per_page: 20, + totalChunks: '123', + } + : { + posts: indexedRows( 20 ), + more: true, + total: 25, + per_page: 20, + totalChunks: '123', + }, + } ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb` ); + + await expect( + page.getByText( 'Indexed Post 1', { exact: true } ) + ).toBeVisible(); + await expect( page.getByText( '25 results' ) ).toBeVisible(); + await expect( page.getByText( '123 chunks' ) ).toBeVisible(); + await expect( page.getByText( 'Page 1 of 2' ) ).toBeVisible(); + + await page.getByRole( 'button', { name: 'Next' } ).click(); + + await expect( page.getByText( 'Indexed Post 21' ) ).toBeVisible(); + await expect( page.getByText( 'Page 2 of 2' ) ).toBeVisible(); + + await page.getByRole( 'button', { name: 'Previous' } ).click(); + await expect( page.getByText( 'Page 1 of 2' ) ).toBeVisible(); + } ); + + test( 'indexing failures surface a warn chip and the stored error', async ( { + page, + admin, + } ) => { + await mockDataApi( page, { + included: () => ( { + posts: [ + { + ID: 1001, + title: 'Broken Post', + type: 'post', + chunks: 0, + error: 'The provider rejected the request.', + }, + ], + more: false, + total: 1, + per_page: 20, + totalChunks: '3', + } ), + } ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb` ); + + await expect( + page.getByText( 'Indexing failed', { exact: true } ) + ).toBeVisible(); + await expect( + page.getByText( + 'Indexing failed: The provider rejected the request.' + ) + ).toBeVisible(); + } ); + + test( 'removing indexed content asks for confirmation first', async ( { + page, + admin, + } ) => { + const deleted = []; + + await mockDataApi( page, { + included: () => ( { + posts: [ + { + ID: 1001, + title: 'Old Landing Page', + type: 'post', + chunks: 2, + }, + ], + more: false, + total: 1, + per_page: 20, + totalChunks: '2', + } ), + onDelete: ( url ) => deleted.push( url ), + } ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb` ); + + await page.getByRole( 'button', { name: 'Remove' } ).click(); + + await expect( + page.getByRole( 'heading', { + name: 'Remove from the Knowledge Base?', + } ) + ).toBeVisible(); + await expect( + page.getByText( 'The content itself stays on your site' ) + ).toBeVisible(); + + // Cancel aborts without a request. + await page.getByRole( 'button', { name: 'Cancel' } ).click(); + expect( deleted ).toHaveLength( 0 ); + + // Confirming fires the DELETE. + await page.getByRole( 'button', { name: 'Remove' } ).first().click(); + await page + .locator( '.hyve-next-modal' ) + .getByRole( 'button', { name: 'Remove' } ) + .click(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + expect( deleted ).toHaveLength( 1 ); + expect( deleted[ 0 ] ).toContain( 'id=1001' ); + } ); + + test( 'public post is added without confirmation', async ( { + page, + admin, + } ) => { + const added = []; + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '0', + }, + onAdd: ( body ) => added.push( body?.data?.ID ), + } ); + await openWordPressDrill( page, admin ); + + await page + .getByRole( 'button', { name: 'Add', exact: true } ) + .nth( 0 ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Add restricted content?' } ) + ).toBeHidden(); + await expect( + page.getByText( 'Added', { exact: true } ) + ).toBeVisible(); + expect( added ).toEqual( [ 201 ] ); + } ); + + test( 'private post asks for confirmation and Cancel aborts', async ( { + page, + admin, + } ) => { + const added = []; + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '0', + }, + onAdd: ( body ) => added.push( body?.data?.ID ), + } ); + await openWordPressDrill( page, admin ); + + await page + .getByRole( 'button', { name: 'Add', exact: true } ) + .nth( 1 ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Add restricted content?' } ) + ).toBeVisible(); + await expect( + page.getByText( 'This is a private post.' ) + ).toBeVisible(); + + await page.getByRole( 'button', { name: 'Cancel' } ).click(); + + await expect( + page.getByRole( 'heading', { name: 'Add restricted content?' } ) + ).toBeHidden(); + expect( added ).toEqual( [] ); + } ); + + test( 'private post is added after confirming', async ( { + page, + admin, + } ) => { + const added = []; + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '0', + }, + onAdd: ( body ) => added.push( body?.data?.ID ), + } ); + await openWordPressDrill( page, admin ); + + await page + .getByRole( 'button', { name: 'Add', exact: true } ) + .nth( 1 ) + .click(); + await page.getByRole( 'button', { name: 'Add anyway' } ).click(); + + await expect( + page.getByText( 'Added', { exact: true } ) + ).toBeVisible(); + expect( added ).toEqual( [ 202 ] ); + } ); + + test( 'password-protected post shows a password-specific warning', async ( { + page, + admin, + } ) => { + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '0', + }, + } ); + await openWordPressDrill( page, admin ); + + await page + .getByRole( 'button', { name: 'Add', exact: true } ) + .nth( 2 ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Add restricted content?' } ) + ).toBeVisible(); + await expect( + page.getByText( 'This is a password-protected post.' ) + ).toBeVisible(); + } ); + + test( 'bulk add with restricted items offers to skip them', async ( { + page, + admin, + } ) => { + const added = []; + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '0', + }, + onAdd: ( body ) => added.push( body?.data?.ID ), + } ); + await openWordPressDrill( page, admin ); + + await page.getByLabel( 'Select all on this page' ).check(); + + await expect( page.getByText( '3 items selected' ) ).toBeVisible(); + + await page + .getByRole( 'button', { name: 'Add 3 to the Knowledge Base' } ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Add restricted content?' } ) + ).toBeVisible(); + await expect( + page.getByText( + '2 selected items are private or password protected' + ) + ).toBeVisible(); + + await page.getByRole( 'button', { name: 'Skip them' } ).click(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + expect( added ).toEqual( [ 201 ] ); + } ); + + test( 'chunk limit disables adding and shows the notice', async ( { + page, + admin, + } ) => { + await mockDataApi( page, { + listing: { + posts: MOCK_POSTS, + more: false, + total: 3, + per_page: 20, + totalChunks: '500', + }, + } ); + await openWordPressDrill( page, admin ); + + await expect( + page.getByText( 'You have reached the limit of posts' ) + ).toBeVisible(); + await expect( + page.getByRole( 'button', { name: 'Add', exact: true } ).nth( 0 ) + ).toBeDisabled(); + } ); + + test( 'needs attention lists both queues and Review opens the moderation modal', async ( { + page, + admin, + } ) => { + await mockDataApi( page, { + counts: { pending: 1, moderation: 1 }, + pending: { + posts: [ { ID: 301, title: 'Edited Post', type: 'post' } ], + more: false, + totalChunks: '5', + }, + moderation: { + posts: [ + { + ID: 302, + title: 'Flagged Post', + type: 'post', + content: 'Flagged content', + review: { hate: 0.5 }, + }, + ], + more: false, + totalChunks: '5', + }, + } ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=kb&sub=attention` ); + + await expect( page.getByText( 'Edited Post' ) ).toBeVisible(); + await expect( page.getByText( 'Flagged Post' ) ).toBeVisible(); + await expect( page.getByText( 'Edited since indexing' ) ).toBeVisible(); + await expect( + page.getByText( 'Failed moderation', { exact: true } ) + ).toBeVisible(); + await expect( + page.getByRole( 'button', { name: 'Update all' } ) + ).toBeVisible(); + + // The badge on the subnav reflects the merged count. + await expect( page.locator( '.hyve-next__subbadge' ) ).toHaveText( + '2' + ); + + await page.getByRole( 'button', { name: 'Review' } ).click(); + + await expect( + page.getByRole( 'heading', { + name: 'Failed Moderation: Flagged Post', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'button', { name: 'Override moderation' } ) + ).toBeVisible(); + } ); +} ); diff --git a/tests/e2e/specs/messages.spec.js b/tests/e2e/specs/messages.spec.js new file mode 100644 index 00000000..7be14f5f --- /dev/null +++ b/tests/e2e/specs/messages.spec.js @@ -0,0 +1,193 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; +import { + mockConfirmDeleteThreadResponse, + mockGetThreadsResponse, +} from '../utils'; + +const HYVE_ADMIN = 'admin.php?page=hyve'; + +test.describe( 'Messages', () => { + test( 'lists conversations with message count and snippet', async ( { + page, + admin, + } ) => { + await mockGetThreadsResponse( page ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=messages` ); + + await expect( + page.getByRole( 'heading', { name: 'Conversations' } ) + ).toBeVisible(); + + // Table headers. + await expect( + page.getByRole( 'columnheader', { name: 'Conversation' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'columnheader', { name: 'Messages' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'columnheader', { name: 'Date' } ) + ).toBeVisible(); + + // A row shows its title, snippet of the last message, and count. + await expect( + page.getByText( 'How to reset my password?' ) + ).toBeVisible(); + await expect( + page.getByText( 'Is there anything else I can help you with?' ) + ).toBeVisible(); + + const firstRow = page + .locator( 'tr', { hasText: 'How to reset my password?' } ) + .first(); + await expect( firstRow.locator( 'td' ).nth( 1 ) ).toHaveText( '6' ); + } ); + + test( 'export CSV is locked on the free plan', async ( { + page, + admin, + } ) => { + await mockGetThreadsResponse( page ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=messages` ); + + await expect( + page.getByRole( 'button', { name: 'Export CSV' } ) + ).toBeDisabled(); + } ); + + test( 'viewing a conversation renders the thread with bubbles and meta', async ( { + page, + admin, + } ) => { + await mockGetThreadsResponse( page ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=messages` ); + + await page.getByRole( 'button', { name: 'View' } ).first().click(); + + await expect( + page.getByRole( 'heading', { name: 'How to reset my password?' } ) + ).toBeVisible(); + + // Meta line: message count and the OpenAI thread id. + await expect( page.getByText( '6 messages' ) ).toBeVisible(); + await expect( + page.getByText( 'S1sTWm3SoQFa5D0LxzNpY9mE' ) + ).toBeVisible(); + + // User and bot bubbles render. + await expect( + page.getByText( 'I did not receive the email' ) + ).toBeVisible(); + await expect( + page.getByText( 'Please check your spam folder' ) + ).toBeVisible(); + + // The back link returns to the list. + await page + .getByRole( 'button', { name: '← All conversations' } ) + .click(); + await expect( + page.getByRole( 'heading', { name: 'Conversations' } ) + ).toBeVisible(); + } ); + + test( 'deleting a conversation asks for confirmation first', async ( { + page, + admin, + } ) => { + await mockGetThreadsResponse( page ); + await mockConfirmDeleteThreadResponse( page ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=messages` ); + + await page.getByRole( 'button', { name: 'View' } ).first().click(); + await page + .getByRole( 'button', { name: 'Delete conversation' } ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Delete this conversation?' } ) + ).toBeVisible(); + + // Cancel keeps the thread open. + await page.getByRole( 'button', { name: 'Cancel' } ).click(); + await expect( + page.getByRole( 'heading', { name: 'How to reset my password?' } ) + ).toBeVisible(); + + // Confirming deletes and returns to the list. + await page + .getByRole( 'button', { name: 'Delete conversation' } ) + .click(); + await page + .locator( '.hyve-next-modal' ) + .getByRole( 'button', { name: 'Delete conversation' } ) + .click(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + await expect( + page.getByRole( 'heading', { name: 'Conversations' } ) + ).toBeVisible(); + } ); + + test( 'an unknown deep-linked conversation shows the not-found state', async ( { + page, + admin, + } ) => { + await mockGetThreadsResponse( page ); + + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=messages&sub=thread&item=99999` + ); + + await expect( + page.getByRole( 'heading', { name: 'Conversation not found' } ) + ).toBeVisible(); + } ); + + test( 'free plan shows the history upsell when more pages exist', async ( { + page, + admin, + } ) => { + await page.route( + /.*(?:rest_route=%2Fhyve%2Fv1%2Fthreads|\/wp-json\/hyve\/v1\/threads).*/, + async ( route ) => { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( { + posts: [ + { + ID: 1, + title: 'First conversation', + date: '2026-07-01T10:00:00+00:00', + thread: [ + { + time: 1751364000, + sender: 'user', + message: 'Hi', + }, + ], + }, + ], + more: true, + total: 12, + per_page: 3, + } ), + } ); + } + ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=messages` ); + + await expect( + page.getByText( 'Read every conversation' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Unlock with Pro' } ) + ).toBeVisible(); + } ); +} ); diff --git a/tests/e2e/specs/privacy-notice.spec.js b/tests/e2e/specs/privacy-notice.spec.js new file mode 100644 index 00000000..17775754 --- /dev/null +++ b/tests/e2e/specs/privacy-notice.spec.js @@ -0,0 +1,142 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; +import { mockChatResponse } from '../utils'; + +const PRIVACY_URL = 'https://example.com/privacy-policy'; + +test.describe( 'Privacy notice', () => { + /** + * Publish a post with an inline chat block and open it on the front end. + * + * The inline variant renders the chat window straight away, so there's no + * launcher to click before the notice is in the DOM. + * + * @param {Object} fixtures Playwright fixtures. + * @param {import('@wordpress/e2e-test-utils-playwright').Admin} fixtures.admin + * @param {import('@wordpress/e2e-test-utils-playwright').Editor} fixtures.editor + * @param {import('@playwright/test').Page} fixtures.page + */ + async function openInlineChat( { admin, editor, page } ) { + await admin.createNewPost( { title: 'Privacy Notice Post' } ); + await editor.insertBlock( { name: 'hyve/chat', attributes: {} } ); + const postId = await editor.publishPost(); + + await page.goto( `?p=${ postId }` ); + await page.waitForFunction( () => window.hyveApp && window.hyveClient ); + } + + /** + * Seed the localized privacy-notice config and (re)render the widget. + * + * The widget may already have auto-rendered on load (when a knowledge base + * exists), so the inline container is cleared first to guarantee a single, + * freshly-configured instance. + * + * @param {import('@playwright/test').Page} page + * @param {{enabled: boolean, url: string}} notice + */ + async function renderWithNotice( page, notice ) { + await page.evaluate( ( config ) => { + window.hyveClient.privacyNotice = config; + const inline = document.querySelector( '#hyve-inline-chat' ); + if ( inline ) { + inline.innerHTML = ''; + } + window.hyveApp.initialize(); + }, notice ); + } + + test( 'renders with a link when enabled and a policy URL exists', async ( { + page, + admin, + editor, + } ) => { + await openInlineChat( { admin, editor, page } ); + await renderWithNotice( page, { enabled: true, url: PRIVACY_URL } ); + + const notice = page.locator( '.hyve-privacy-notice' ); + + await expect( notice ).toBeVisible(); + await expect( notice ).toContainText( 'By chatting, you agree to our' ); + + const link = notice.locator( '.hyve-privacy-notice__link' ); + await expect( link ).toHaveText( 'Privacy Policy' ); + await expect( link ).toHaveAttribute( 'href', PRIVACY_URL ); + await expect( link ).toHaveAttribute( 'target', '_blank' ); + } ); + + test( 'does not render without a policy URL', async ( { + page, + admin, + editor, + } ) => { + await openInlineChat( { admin, editor, page } ); + await renderWithNotice( page, { enabled: true, url: '' } ); + + await expect( page.locator( '.hyve-privacy-notice' ) ).toHaveCount( 0 ); + } ); + + test( 'does not render when disabled', async ( { + page, + admin, + editor, + } ) => { + await openInlineChat( { admin, editor, page } ); + await renderWithNotice( page, { enabled: false, url: PRIVACY_URL } ); + + await expect( page.locator( '.hyve-privacy-notice' ) ).toHaveCount( 0 ); + } ); + + test( 'dismiss button removes it and remembers the choice', async ( { + page, + admin, + editor, + } ) => { + await openInlineChat( { admin, editor, page } ); + await renderWithNotice( page, { enabled: true, url: PRIVACY_URL } ); + + const notice = page.locator( '.hyve-privacy-notice' ); + await expect( notice ).toBeVisible(); + + await notice.locator( '.hyve-privacy-notice__dismiss' ).click(); + await expect( notice ).toHaveCount( 0 ); + + const dismissed = await page.evaluate( () => + window.localStorage.getItem( 'hyve-privacy-dismissed' ) + ); + expect( dismissed ).toBe( 'true' ); + + // The choice sticks across reloads. + await page.reload(); + await page.waitForFunction( () => window.hyveApp && window.hyveClient ); + await renderWithNotice( page, { enabled: true, url: PRIVACY_URL } ); + + await expect( page.locator( '.hyve-privacy-notice' ) ).toHaveCount( 0 ); + } ); + + test( 'sending a message dismisses the notice', async ( { + page, + admin, + editor, + } ) => { + await openInlineChat( { admin, editor, page } ); + await renderWithNotice( page, { enabled: true, url: PRIVACY_URL } ); + await mockChatResponse( page ); + + await expect( page.locator( '.hyve-privacy-notice' ) ).toBeVisible(); + + await page + .getByRole( 'textbox', { name: 'Write a reply…' } ) + .fill( 'Hello' ); + await page + .locator( '#hyve-send-button' ) + .getByRole( 'button' ) + .click( { force: true } ); + + await expect( page.locator( '.hyve-privacy-notice' ) ).toHaveCount( 0 ); + + const dismissed = await page.evaluate( () => + window.localStorage.getItem( 'hyve-privacy-dismissed' ) + ); + expect( dismissed ).toBe( 'true' ); + } ); +} ); diff --git a/tests/e2e/specs/settings.spec.js b/tests/e2e/specs/settings.spec.js new file mode 100644 index 00000000..0c830590 --- /dev/null +++ b/tests/e2e/specs/settings.spec.js @@ -0,0 +1,303 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; +import { HYVE_SETTINGS_API_ROUTE_PATTERN } from '../utils'; + +const HYVE_ADMIN = 'admin.php?page=hyve'; + +/** + * Capture settings saves; GET requests pass through to the real endpoint. + * + * @param {import("@playwright/test").Page} page The page. + * @param {Object[]} saves Captured POST payloads. + */ +async function captureSettingsSaves( page, saves ) { + await page.route( HYVE_SETTINGS_API_ROUTE_PATTERN, async ( route ) => { + const request = route.request(); + + if ( 'POST' === request.method() ) { + saves.push( request.postDataJSON() ); + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( { + success: 'Settings saved.', + } ), + } ); + return; + } + + await route.continue(); + } ); +} + +test.describe( 'Settings', () => { + test( 'side nav renders grouped panels with the default active', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + const nav = page.getByRole( 'navigation', { + name: 'Settings sections', + } ); + + // Group headings. + await expect( nav.getByText( 'Chat', { exact: true } ) ).toBeVisible(); + await expect( nav.getByText( 'AI', { exact: true } ) ).toBeVisible(); + await expect( nav.getByText( 'Integrations' ) ).toBeVisible(); + await expect( nav.getByText( 'Plugin' ) ).toBeVisible(); + + // Behavior is the default panel. + await expect( + nav.getByRole( 'button', { name: 'Behavior' } ) + ).toHaveAttribute( 'aria-current', 'page' ); + await expect( + page.getByText( 'Where should Hyve appear?' ).first() + ).toBeVisible(); + } ); + + test( 'visibility: select a mode, add a URL rule and save', async ( { + page, + admin, + } ) => { + const saves = []; + await captureSettingsSaves( page, saves ); + + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + await expect( + page.getByRole( 'radio', { name: 'Show on all pages' } ) + ).toBeVisible(); + + // The URL box only shows for include/exclude modes. + await expect( page.getByText( 'Content URLs' ) ).toBeHidden(); + + await page + .getByRole( 'radio', { name: 'Only on selected content' } ) + .check(); + + await expect( page.getByText( 'Content URLs' ) ).toBeVisible(); + + await page.getByRole( 'button', { name: 'Add URL rule' } ).click(); + await page.getByPlaceholder( '/example-page/' ).fill( '/shop/' ); + + await page + .getByRole( 'button', { name: 'Save changes' } ) + .first() + .click(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + + expect( saves[ 0 ]?.data?.display_mode ).toBe( 'include' ); + expect( saves[ 0 ]?.data?.display_rules?.[ 0 ]?.path ).toBe( '/shop/' ); + } ); + + test( 'visibility: manual mode hides the URL box', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + await page + .getByRole( 'radio', { name: "Don't show automatically" } ) + .check(); + + await expect( page.getByText( 'Content URLs' ) ).toBeHidden(); + } ); + + test( 'behavior: suggestions are locked on the free plan', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + await expect( + page.getByText( 'Greet visitors with ready-made questions' ) + ).toBeVisible(); + await expect( + page.getByLabel( 'Suggested question 1' ) + ).toBeDisabled(); + } ); + + test( 'behavior: proactive message is locked on the free plan', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + const card = page.locator( '.hyve-next-card', { + hasText: 'Proactive message', + } ); + + await expect( + card.getByText( 'Start conversations before visitors do' ) + ).toBeVisible(); + await expect( + card.getByRole( 'link', { name: 'Unlock with Pro' } ) + ).toBeVisible(); + + // The trigger select is locked on Disabled, so the invite message + // field stays hidden and no teaser can ever be configured. + await expect( card.getByLabel( 'Trigger' ) ).toBeDisabled(); + await expect( card.getByLabel( 'Trigger' ) ).toHaveValue( 'none' ); + await expect( card.getByLabel( 'Invite message' ) ).toBeHidden(); + } ); + + test( 'behavior: privacy notice warns when no policy page is set', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings` ); + + const row = page + .locator( '.hyve-next-field' ) + .filter( { hasText: 'Privacy notice' } ); + + // Enabling the toggle surfaces the missing-page warning (the test + // site has no Privacy Policy page configured). + await row.getByRole( 'checkbox' ).check(); + + await expect( + page.getByText( 'No Privacy Policy page is set' ) + ).toBeVisible(); + } ); + + test( 'appearance: pro fields are locked and the position toggle works', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=chat-appearance` + ); + + await expect( + page.getByRole( 'heading', { name: 'Widget' } ) + ).toBeVisible(); + await expect( + page.getByText( + 'A custom name, icon and colors are part of Hyve Pro.' + ) + ).toBeVisible(); + await expect( page.getByLabel( 'Assistant name' ) ).toBeDisabled(); + + // Position is a free control. + await expect( + page.getByRole( 'radio', { name: 'Left' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'radio', { name: 'Right' } ) + ).toBeVisible(); + } ); + + test( 'provider: API key status flips to Unsaved while editing, model saves', async ( { + page, + admin, + } ) => { + const saves = []; + await captureSettingsSaves( page, saves ); + + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=ai-provider` + ); + + await expect( + page.getByRole( 'heading', { name: 'OpenAI' } ) + ).toBeVisible(); + + // The dummy test key counts as connected until an API error is stored. + await expect( + page.getByText( 'Connected', { exact: true } ) + ).toBeVisible(); + + await page.getByLabel( 'API key' ).fill( 'sk_new_key' ); + await expect( page.getByText( 'Unsaved' ) ).toBeVisible(); + + await page.getByLabel( 'Model' ).selectOption( 'gpt-4.1-nano' ); + await page.getByRole( 'button', { name: 'Save changes' } ).click(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + expect( saves[ 0 ]?.data?.api_key ).toBe( 'sk_new_key' ); + expect( saves[ 0 ]?.data?.chat_model ).toBe( 'gpt-4.1-nano' ); + } ); + + test( 'advanced: sliders render and reset restores the defaults', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=ai-advanced` + ); + + await expect( + page.getByRole( 'heading', { name: 'Advanced tuning' } ) + ).toBeVisible(); + + const similarity = page + .getByRole( 'slider', { name: 'Similarity threshold' } ) + .first(); + await expect( similarity ).toBeVisible(); + + await similarity.fill( '0.8' ); + await page.getByRole( 'button', { name: 'Reset to defaults' } ).click(); + + await expect( similarity ).toHaveValue( '0.4' ); + } ); + + test( 'general: toggles save automatically', async ( { page, admin } ) => { + const saves = []; + await captureSettingsSaves( page, saves ); + + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=general` + ); + + await expect( + page.getByRole( 'heading', { name: 'Site integration' } ) + ).toBeVisible(); + + const telemetryRow = page + .locator( '.hyve-next-field' ) + .filter( { hasText: 'Telemetry' } ); + + await telemetryRow.getByRole( 'checkbox' ).check(); + + await expect( page.getByTestId( 'snackbar' ) ).toBeVisible(); + expect( saves[ 0 ]?.data?.telemetry_enabled ).toBe( true ); + } ); + + test( 'qdrant: connect form renders with its status chip', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( `${ HYVE_ADMIN }&nav=settings&sub=qdrant` ); + + await expect( + page.getByRole( 'heading', { name: 'Qdrant' } ) + ).toBeVisible(); + await expect( page.getByText( 'Not connected' ) ).toBeVisible(); + await expect( page.getByLabel( 'Qdrant API key' ) ).toBeVisible(); + await expect( page.getByLabel( 'Qdrant endpoint' ) ).toBeVisible(); + await expect( + page.getByRole( 'button', { name: 'Connect' } ) + ).toBeVisible(); + } ); + + test( 'api access: free plan sees the upsell and the curl example', async ( { + page, + admin, + } ) => { + await admin.visitAdminPage( + `${ HYVE_ADMIN }&nav=settings&sub=api-access` + ); + + await expect( + page.getByRole( 'heading', { name: 'API access' } ) + ).toBeVisible(); + await expect( page.locator( '.hyve-next-code' ) ).toBeVisible(); + await expect( + page.getByText( 'Search your Knowledge Base from anywhere' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Unlock with Pro' } ) + ).toBeVisible(); + } ); +} ); diff --git a/tests/e2e/utils.js b/tests/e2e/utils.js index f4b441e6..3f02f91e 100644 --- a/tests/e2e/utils.js +++ b/tests/e2e/utils.js @@ -3,146 +3,214 @@ * * @param {import("@playwright/test").Page} page The page. */ +const HYVE_THREADS_API_ROUTE_PATTERN = + /.*(?:rest_route=%2Fhyve%2Fv1%2Fthreads|\/wp-json\/hyve\/v1\/threads).*/; +const HYVE_CHAT_API_ROUTE_PATTERN = + /.*(?:rest_route=%2Fhyve%2Fv1%2Fchat|\/wp-json\/hyve\/v1\/chat).*/; + +export const HYVE_DATA_API_ROUTE_PATTERN = + /.*(?:rest_route=%2Fhyve%2Fv1%2Fdata|\/wp-json\/hyve\/v1\/data).*/; + +export const HYVE_SETTINGS_API_ROUTE_PATTERN = + /.*(?:rest_route=%2Fhyve%2Fv1%2Fsettings|\/wp-json\/hyve\/v1\/settings).*/; + +const HYVE_STATS_API_ROUTE_PATTERN = + /.*(?:rest_route=%2Fhyve%2Fv1%2Fstats|\/wp-json\/hyve\/v1\/stats).*/; + +/** + * Detect the Needs Attention count endpoint, which the data pattern above + * also matches (`data/counts` contains `data`). + * + * @param {string} url The request URL. + * @return {boolean} Whether the request targets `data/counts`. + */ +export const isDataCountsRequest = ( url ) => + url.includes( 'data%2Fcounts' ) || url.includes( 'data/counts' ); + +/** + * Mock the response for the `/stats` endpoint the new dashboard fetches on + * mount. + * + * @param {import("@playwright/test").Page} page The page. + * @param {Object} [options] + * @param {number} [options.threads] Session count. + * @param {number} [options.messages] Message count. + * @param {number} [options.totalChunks] Knowledge base chunk count. + * @param {?Object} [options.chart] Chart payload; defaults to an empty chart. + */ +export async function mockStatsResponse( + page, + { threads = 0, messages = 0, totalChunks = 0, chart = null } = {} +) { + await page.route( HYVE_STATS_API_ROUTE_PATTERN, async ( route ) => { + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( { + stats: { threads, messages, totalChunks }, + chart: chart ?? { + legend: { + messagesLabel: 'User Messages per Day', + sessionsLabel: 'Active Sessions per Day', + }, + data: { messages: [], sessions: [] }, + labels: [], + }, + } ), + } ); + } ); +} + export async function mockGetThreadsResponse( page ) { - await page.route( - /.*rest_route=%2Fhyve%2Fv1%2Fthreads.*offset=0.*/, - async ( route ) => { + await page.route( HYVE_THREADS_API_ROUTE_PATTERN, async ( route ) => { + const url = route.request().url(); + + // Only the first page is mocked; the dashboard widget requests it + // without an offset parameter. + if ( url.includes( 'offset=' ) && ! url.includes( 'offset=0' ) ) { + await route.continue(); + return; + } + await route.fulfill( { + status: 200, + contentType: 'application/json', + body: JSON.stringify( { + posts: [ + { + ID: 150, + title: 'How to reset my password?', + date: '2025-05-26T13:02:20+00:00', + thread: [ + { + time: 1748264540, + sender: 'user', + message: 'How do I reset my password?', + }, + { + time: 1748264544, + sender: 'bot', + message: + '

You can reset your password by going to the login page and clicking "Forgot Password". Enter your email address and follow the instructions sent to your inbox.

', + }, + { + time: 1748265203, + sender: 'user', + message: 'I did not receive the email', + }, + { + time: 1748265207, + sender: 'bot', + message: + '

Please check your spam folder. If you still do not see it, make sure you entered the correct email address associated with your account.

', + }, + { + time: 1748265217, + sender: 'user', + message: 'Thanks, found it in spam!', + }, + { + time: 1748267412, + sender: 'bot', + message: + '

Great! Is there anything else I can help you with?

', + }, + ], + thread_id: 'thread_S1sTWm3SoQFa5D0LxzNpY9mE', + }, + { + ID: 149, + title: 'Order status inquiry', + date: '2025-05-26T13:02:11+00:00', + thread: [ + { + time: 1748264531, + sender: 'user', + message: + 'What is the status of my order #12345?', + }, + { + time: 1748264535, + sender: 'bot', + message: + '

Your order #12345 has been shipped and should arrive within 2-3 business days. You can track it using the tracking number sent to your email.

', + }, + ], + thread_id: 'thread_omANmzE1oYI6FFDfc3j3pCQB', + }, + { + ID: 148, + title: 'Product availability', + date: '2025-05-26T13:02:06+00:00', + thread: [ + { + time: 1748264526, + sender: 'user', + message: + 'Is the red t-shirt in size M available?', + }, + { + time: 1748264530, + sender: 'bot', + message: + '

Yes, the red t-shirt in size M is currently in stock. Would you like me to help you add it to your cart?

', + }, + ], + thread_id: 'thread_lFSy430KC6bJ3kxsAr8SNZrv', + }, + { + ID: 147, + title: 'Return policy question', + date: '2025-05-26T13:01:37+00:00', + thread: [ + { + time: 1748264497, + sender: 'user', + message: 'What is your return policy?', + }, + { + time: 1748264501, + sender: 'bot', + message: + '

We offer a 30-day return policy for unused items in original packaging. You can initiate a return from your account dashboard.

', + }, + ], + thread_id: 'thread_QG9UaCmeYI5ItcTP7Gzgz40D', + }, + ], + more: false, + } ), + } ); + } ); +} + +export async function mockConfirmDeleteThreadResponse( page ) { + await page.route( HYVE_THREADS_API_ROUTE_PATTERN, async ( route ) => { + const request = route.request(); + + // Fall back so other registered thread mocks handle non-delete + // requests; continue() would bypass them and hit the network. + if ( ! /[?&]id=\d+/.test( request.url() ) ) { + await route.fallback(); + return; + } + + // Check if this is a DELETE request (via POST with method override) + if ( + request.method() === 'POST' && + request.headers()[ 'x-http-method-override' ] === 'DELETE' + ) { await route.fulfill( { status: 200, contentType: 'application/json', body: JSON.stringify( { - posts: [ - { - ID: 150, - title: 'How to reset my password?', - date: '2025-05-26T13:02:20+00:00', - thread: [ - { - time: 1748264540, - sender: 'user', - message: 'How do I reset my password?', - }, - { - time: 1748264544, - sender: 'bot', - message: - '

You can reset your password by going to the login page and clicking "Forgot Password". Enter your email address and follow the instructions sent to your inbox.

', - }, - { - time: 1748265203, - sender: 'user', - message: 'I did not receive the email', - }, - { - time: 1748265207, - sender: 'bot', - message: - '

Please check your spam folder. If you still do not see it, make sure you entered the correct email address associated with your account.

', - }, - { - time: 1748265217, - sender: 'user', - message: 'Thanks, found it in spam!', - }, - { - time: 1748267412, - sender: 'bot', - message: - '

Great! Is there anything else I can help you with?

', - }, - ], - thread_id: 'thread_S1sTWm3SoQFa5D0LxzNpY9mE', - }, - { - ID: 149, - title: 'Order status inquiry', - date: '2025-05-26T13:02:11+00:00', - thread: [ - { - time: 1748264531, - sender: 'user', - message: - 'What is the status of my order #12345?', - }, - { - time: 1748264535, - sender: 'bot', - message: - '

Your order #12345 has been shipped and should arrive within 2-3 business days. You can track it using the tracking number sent to your email.

', - }, - ], - thread_id: 'thread_omANmzE1oYI6FFDfc3j3pCQB', - }, - { - ID: 148, - title: 'Product availability', - date: '2025-05-26T13:02:06+00:00', - thread: [ - { - time: 1748264526, - sender: 'user', - message: - 'Is the red t-shirt in size M available?', - }, - { - time: 1748264530, - sender: 'bot', - message: - '

Yes, the red t-shirt in size M is currently in stock. Would you like me to help you add it to your cart?

', - }, - ], - thread_id: 'thread_lFSy430KC6bJ3kxsAr8SNZrv', - }, - { - ID: 147, - title: 'Return policy question', - date: '2025-05-26T13:01:37+00:00', - thread: [ - { - time: 1748264497, - sender: 'user', - message: 'What is your return policy?', - }, - { - time: 1748264501, - sender: 'bot', - message: - '

We offer a 30-day return policy for unused items in original packaging. You can initiate a return from your account dashboard.

', - }, - ], - thread_id: 'thread_QG9UaCmeYI5ItcTP7Gzgz40D', - }, - ], - more: false, + success: true, + data: 'Thread removed from local storage. It remains accessible via the OpenAI API.', } ), } ); + } else { + await route.fallback(); } - ); -} - -export async function mockConfirmDeleteThreadResponse( page ) { - await page.route( - /.*rest_route=%2Fhyve%2Fv1%2Fthreads.*id=\d+.*/, - async ( route ) => { - const request = route.request(); - // Check if this is a DELETE request (via POST with method override) - if ( - request.method() === 'POST' && - request.headers()[ 'x-http-method-override' ] === 'DELETE' - ) { - await route.fulfill( { - status: 200, - contentType: 'application/json', - body: JSON.stringify( { - success: true, - data: 'Thread removed from local storage. It remains accessible via the OpenAI API.', - } ), - } ); - } else { - await route.continue(); - } - } - ); + } ); } /** @@ -162,7 +230,7 @@ export async function mockChatResponse( status = 'completed', } = {} ) { - await page.route( /.*rest_route=%2Fhyve%2Fv1%2Fchat.*/, async ( route ) => { + await page.route( HYVE_CHAT_API_ROUTE_PATTERN, async ( route ) => { await new Promise( ( r ) => setTimeout( r, delay ) ); await route.fulfill( { status: 200, diff --git a/tests/php/unit/tests/test-api-key-connected.php b/tests/php/unit/tests/test-api-key-connected.php new file mode 100644 index 00000000..1e70ed9d --- /dev/null +++ b/tests/php/unit/tests/test-api-key-connected.php @@ -0,0 +1,82 @@ +assertFalse( Main::is_api_key_connected( [] ) ); + $this->assertFalse( Main::is_api_key_connected( [ 'api_key' => '' ] ) ); + } + + /** + * A key with no stored error is connected. + */ + public function testKeyWithoutErrorIsConnected() { + delete_option( OpenAI::ERROR_OPTION_KEY ); + + $this->assertTrue( Main::is_api_key_connected( [ 'api_key' => 'sk-test' ] ) ); + } + + /** + * A key-invalidating error reports as not connected. + */ + public function testKeyErrorIsNotConnected() { + $codes = [ + 'invalid_api_key', + 'invalid_authentication', + 'account_deactivated', + 'billing_not_active', + 'organization_not_found', + 'organization_deactivated', + 'permission_denied', + 'insufficient_quota', + ]; + + foreach ( $codes as $code ) { + update_option( OpenAI::ERROR_OPTION_KEY, [ 'code' => $code ] ); + + $this->assertFalse( + Main::is_api_key_connected( [ 'api_key' => 'sk-test' ] ), + "Code {$code} should mark the key as not connected." + ); + } + } + + /** + * A transient error (e.g. rate limiting) keeps the key connected. + */ + public function testTransientErrorStaysConnected() { + update_option( OpenAI::ERROR_OPTION_KEY, [ 'code' => 'rate_limit_exceeded' ] ); + + $this->assertTrue( Main::is_api_key_connected( [ 'api_key' => 'sk-test' ] ) ); + } + + /** + * A stored error without a code does not invalidate the key. + */ + public function testErrorWithoutCodeStaysConnected() { + update_option( OpenAI::ERROR_OPTION_KEY, [ 'message' => 'Something happened.' ] ); + + $this->assertTrue( Main::is_api_key_connected( [ 'api_key' => 'sk-test' ] ) ); + } +} diff --git a/tests/php/unit/tests/test-db.php b/tests/php/unit/tests/test-db.php index cb88de3f..32afee11 100644 --- a/tests/php/unit/tests/test-db.php +++ b/tests/php/unit/tests/test-db.php @@ -88,4 +88,18 @@ public function test_get_posts_over_limit() { $posts = $this->db_table->get_posts_over_limit(); $this->assertCount( 100, $posts ); } + + /** + * Test get_post_id. + */ + public function test_get_post_id() { + $insert_id = $this->db_table->insert( + [ + 'post_content' => 'Source content.', + 'post_id' => 777, + ] + ); + + $this->assertEquals( '777', (string) $this->db_table->get_post_id( $insert_id ) ); + } } diff --git a/tests/php/unit/tests/test-encryption.php b/tests/php/unit/tests/test-encryption.php new file mode 100644 index 00000000..c3c87161 --- /dev/null +++ b/tests/php/unit/tests/test-encryption.php @@ -0,0 +1,137 @@ +assertIsString( $encrypted ); + $this->assertTrue( Encryption::is_encrypted( $encrypted ) ); + $this->assertStringNotContainsString( 'sk-secret', $encrypted ); + $this->assertSame( 'sk-secret', Encryption::decrypt( $encrypted ) ); + } + + /** + * Legacy plaintext remains readable until migration runs. + */ + public function test_plaintext_is_returned_unchanged() { + $this->assertSame( 'legacy-key', Encryption::decrypt( 'legacy-key' ) ); + } + + /** + * Plaintext values resembling the old suffix format are still encrypted. + */ + public function test_plaintext_suffix_is_not_treated_as_encrypted() { + $encrypted = Encryption::encrypt( 'sk-secret_enc' ); + + $this->assertNotSame( 'sk-secret_enc', $encrypted ); + $this->assertTrue( Encryption::is_encrypted( $encrypted ) ); + $this->assertSame( 'sk-secret_enc', Encryption::decrypt( $encrypted ) ); + } + + /** + * An unreadable key marker detects changed WordPress salts. + */ + public function test_changed_key_is_detected() { + update_option( Encryption::KEY_CHECK_OPTION, 'invalid-payload_enc' ); + + $this->assertTrue( Encryption::has_key_changed() ); + } + + /** + * Settings expose decrypted credentials to existing consumers. + */ + public function test_get_settings_decrypts_credentials() { + update_option( + 'hyve_settings', + [ + 'api_key' => Encryption::encrypt( 'sk-openai' ), + 'qdrant_api_key' => Encryption::encrypt( 'qdrant-secret' ), + ] + ); + + $settings = Main::get_settings(); + + $this->assertSame( 'sk-openai', $settings['api_key'] ); + $this->assertSame( 'qdrant-secret', $settings['qdrant_api_key'] ); + } + + /** + * Saving decrypted settings keeps the credentials encrypted in storage. + */ + public function test_save_settings_encrypts_credentials() { + $this->assertTrue( + Main::save_settings( + [ + 'api_key' => 'sk-openai', + 'qdrant_api_key' => '', + ] + ) + ); + + $stored = get_option( 'hyve_settings' ); + + $this->assertTrue( Encryption::is_encrypted( $stored['api_key'] ) ); + $this->assertSame( 'sk-openai', Encryption::decrypt( $stored['api_key'] ) ); + } + + /** + * A key-change marker remains while an encrypted credential is unreadable. + */ + public function test_key_check_is_not_reset_while_credentials_are_unreadable() { + update_option( Encryption::KEY_CHECK_OPTION, 'invalid-payload' ); + update_option( 'hyve_settings', [ 'api_key' => Encryption::PREFIX . base64_encode( str_repeat( 'x', 29 ) ) ] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode + + $this->assertFalse( Main::can_reset_encryption_key_check( true ) ); + } + + /** + * The upgrade migration encrypts legacy settings. + */ + public function test_migration_encrypts_plaintext_settings() { + update_option( + 'hyve_settings', + [ + 'api_key' => 'sk-openai', + 'qdrant_api_key' => 'qdrant-secret', + ] + ); + + if ( ! class_exists( '\ThemeisleSDK\Modules\Abstract_Migration' ) ) { + require_once HYVE_LITE_PATH . '/vendor/codeinwp/themeisle-sdk/src/Modules/Abstract_Migration.php'; + } + + $migration = require HYVE_LITE_PATH . '/migrations/20260711000000_encrypt_sensitive_settings.php'; + + $this->assertTrue( $migration->should_run() ); + $migration->up(); + + $stored = get_option( 'hyve_settings' ); + $this->assertTrue( Encryption::is_encrypted( $stored['api_key'] ) ); + $this->assertTrue( Encryption::is_encrypted( $stored['qdrant_api_key'] ) ); + $this->assertFalse( $migration->should_run() ); + } +} diff --git a/tests/php/unit/tests/test-openai-model.php b/tests/php/unit/tests/test-openai-model.php new file mode 100644 index 00000000..c076a9f7 --- /dev/null +++ b/tests/php/unit/tests/test-openai-model.php @@ -0,0 +1,40 @@ +assertEquals( 'gpt-5.4-nano', OpenAI::resolve_chat_model( 'gpt-3.5-turbo-0125' ) ); + $this->assertEquals( 'gpt-5.4-nano', OpenAI::resolve_chat_model( 'gpt-3.5-turbo' ) ); + } + + /** + * Empty or invalid values fall back to the default. + */ + public function test_empty_or_invalid_falls_back_to_default() { + $this->assertEquals( 'gpt-5.4-nano', OpenAI::resolve_chat_model( '' ) ); + $this->assertEquals( 'gpt-5.4-nano', OpenAI::resolve_chat_model( null ) ); + $this->assertEquals( 'gpt-5.4-nano', OpenAI::resolve_chat_model( [] ) ); + } + + /** + * Supported models are returned unchanged. + */ + public function test_supported_models_pass_through() { + foreach ( [ 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna', 'gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gpt-5', 'gpt-4.1', 'gpt-4o-mini', 'gpt-4o' ] as $model ) { + $this->assertEquals( $model, OpenAI::resolve_chat_model( $model ) ); + } + } +} diff --git a/tests/php/unit/tests/test-privacy-notice.php b/tests/php/unit/tests/test-privacy-notice.php new file mode 100644 index 00000000..09f13f2f --- /dev/null +++ b/tests/php/unit/tests/test-privacy-notice.php @@ -0,0 +1,107 @@ +main = new Main(); + } + + /** + * Clean up. + */ + protected function tearDown(): void { + delete_option( 'hyve_settings' ); + delete_option( 'wp_page_for_privacy_policy' ); + parent::tearDown(); + } + + /** + * The privacy notice is off by default. + */ + public function test_disabled_by_default() { + $data = $this->main->get_frontend_data(); + + $this->assertFalse( $data['privacyNotice']['enabled'] ); + } + + /** + * Enabling the setting flags the notice as enabled in the frontend data. + */ + public function test_enabled_when_setting_on() { + update_option( 'hyve_settings', [ 'privacy_notice_enabled' => true ] ); + + $data = $this->main->get_frontend_data(); + + $this->assertTrue( $data['privacyNotice']['enabled'] ); + } + + /** + * The notice URL defaults to the site's Privacy Policy page. + */ + public function test_url_uses_privacy_policy_page() { + $page_id = self::factory()->post->create( [ 'post_type' => 'page' ] ); + update_option( 'wp_page_for_privacy_policy', $page_id ); + + $data = $this->main->get_frontend_data(); + + $this->assertSame( get_privacy_policy_url(), $data['privacyNotice']['url'] ); + $this->assertNotEmpty( $data['privacyNotice']['url'] ); + } + + /** + * The URL can be overridden with the dedicated filter. + */ + public function test_url_filterable() { + add_filter( 'hyve_privacy_notice_url', fn() => 'https://example.com/privacy' ); + + $data = $this->main->get_frontend_data(); + + $this->assertSame( 'https://example.com/privacy', $data['privacyNotice']['url'] ); + + remove_all_filters( 'hyve_privacy_notice_url' ); + } + + /** + * The notice sentence keeps a single %s placeholder for the link. + */ + public function test_text_has_link_placeholder() { + $data = $this->main->get_frontend_data(); + + $this->assertStringContainsString( '%s', $data['strings']['privacyNotice'] ); + $this->assertNotEmpty( $data['strings']['privacyPolicy'] ); + } + + /** + * The notice text can be customized with a filter. + */ + public function test_text_filterable() { + add_filter( 'hyve_privacy_notice_text', fn() => 'Custom notice %s here.' ); + + $data = $this->main->get_frontend_data(); + + $this->assertSame( 'Custom notice %s here.', $data['strings']['privacyNotice'] ); + + remove_all_filters( 'hyve_privacy_notice_text' ); + } +} diff --git a/tests/php/unit/tests/test-qdrant.php b/tests/php/unit/tests/test-qdrant.php new file mode 100644 index 00000000..3f0c570f --- /dev/null +++ b/tests/php/unit/tests/test-qdrant.php @@ -0,0 +1,89 @@ +setAccessible( true ); + + return $method->invoke( $qdrant, new Exception( $message, $code ) ); + } + + /** + * Clean up options between tests. + */ + protected function tearDown(): void { + delete_option( Qdrant_API::ERROR_OPTION_KEY ); + delete_option( 'hyve_qdrant_status' ); + parent::tearDown(); + } + + /** + * A missing collection/cluster (404) persists the error and marks the + * connection inactive so the UI stops reporting a false connection. + */ + public function test_not_found_persists_error_and_deactivates() { + update_option( 'hyve_qdrant_status', 'active' ); + + $result = $this->handle( 404, 'Not Found' ); + $saved = get_option( Qdrant_API::ERROR_OPTION_KEY ); + + $this->assertWPError( $result ); + $this->assertEquals( 404, $saved['code'] ); + $this->assertEquals( 'Qdrant', $saved['provider'] ); + $this->assertEquals( 'inactive', get_option( 'hyve_qdrant_status' ) ); + } + + /** + * Revoked credentials (403) also deactivate the connection. + */ + public function test_forbidden_deactivates() { + update_option( 'hyve_qdrant_status', 'active' ); + + $this->handle( 403 ); + + $this->assertEquals( 'inactive', get_option( 'hyve_qdrant_status' ) ); + } + + /** + * Transient errors (e.g. 500) surface in the dashboard but must NOT + * disconnect a working integration. + */ + public function test_transient_error_persists_but_keeps_status() { + update_option( 'hyve_qdrant_status', 'active' ); + + $this->handle( 500, 'Server error' ); + $saved = get_option( Qdrant_API::ERROR_OPTION_KEY ); + + $this->assertEquals( 500, $saved['code'] ); + $this->assertEquals( 'active', get_option( 'hyve_qdrant_status' ) ); + } + + /** + * Known codes map to actionable messages; unknown codes return null. + */ + public function test_error_message_for_code() { + $this->assertNotNull( Qdrant_API::get_error_message_for_code( 403 ) ); + $this->assertNotNull( Qdrant_API::get_error_message_for_code( 404 ) ); + $this->assertNull( Qdrant_API::get_error_message_for_code( 500 ) ); + } +} diff --git a/tests/php/unit/tests/test-source-link.php b/tests/php/unit/tests/test-source-link.php new file mode 100644 index 00000000..ffed5887 --- /dev/null +++ b/tests/php/unit/tests/test-source-link.php @@ -0,0 +1,229 @@ +setAccessible( true ); + + return $method->invoke( $api, $post_id ); + } + + /** + * Public posts resolve to their permalink. + */ + public function test_public_post_returns_permalink() { + $post_id = self::factory()->post->create( [ 'post_status' => 'publish' ] ); + + $this->assertSame( get_permalink( $post_id ), $this->resolve( $post_id ) ); + } + + /** + * Private posts are not linked. + */ + public function test_private_post_returns_empty() { + $post_id = self::factory()->post->create( [ 'post_status' => 'private' ] ); + + $this->assertSame( '', $this->resolve( $post_id ) ); + } + + /** + * Password-protected posts are not linked. + */ + public function test_password_protected_post_returns_empty() { + $post_id = self::factory()->post->create( + [ + 'post_status' => 'publish', + 'post_password' => 'secret', + ] + ); + + $this->assertSame( '', $this->resolve( $post_id ) ); + } + + /** + * The hyve_chat_source_link filter can override the resolved link, + * e.g. for the pro plugin's website-URL sources. + */ + public function test_filter_overrides_link() { + $post_id = self::factory()->post->create( [ 'post_status' => 'private' ] ); + + add_filter( + 'hyve_chat_source_link', + function () { + return 'https://example.com/source'; + } + ); + + $this->assertSame( 'https://example.com/source', $this->resolve( $post_id ) ); + + remove_all_filters( 'hyve_chat_source_link' ); + } + + /** + * The filter can suppress a link, e.g. for custom data. + */ + public function test_filter_can_suppress_link() { + $post_id = self::factory()->post->create( [ 'post_status' => 'publish' ] ); + + add_filter( 'hyve_chat_source_link', '__return_empty_string' ); + + $this->assertSame( '', $this->resolve( $post_id ) ); + + remove_all_filters( 'hyve_chat_source_link' ); + } + + /** + * Count the rendered source links in a response. + * + * @param string $html The response HTML. + * + * @return int + */ + private function count_links( $html ) { + return substr_count( $html, 'hyve-source__link' ); + } + + /** + * Multiple public sources render multiple links, up to the default of 3. + */ + public function test_appends_multiple_links_up_to_default_limit() { + $post_ids = self::factory()->post->create_many( 5, [ 'post_status' => 'publish' ] ); + + $response = API::instance()->maybe_append_source_link( 'Answer.', $post_ids ); + + $this->assertStringContainsString( 'hyve-source', $response ); + $this->assertSame( 3, $this->count_links( $response ) ); + } + + /** + * The hyve_source_link_limit filter controls how many links are shown. + */ + public function test_limit_filter_controls_link_count() { + $post_ids = self::factory()->post->create_many( 4, [ 'post_status' => 'publish' ] ); + + add_filter( 'hyve_source_link_limit', fn() => 2 ); + $response = API::instance()->maybe_append_source_link( 'Answer.', $post_ids ); + $this->assertSame( 2, $this->count_links( $response ) ); + remove_all_filters( 'hyve_source_link_limit' ); + } + + /** + * A limit of zero suppresses the source links entirely. + */ + public function test_zero_limit_suppresses_links() { + $post_ids = self::factory()->post->create_many( 3, [ 'post_status' => 'publish' ] ); + + add_filter( 'hyve_source_link_limit', '__return_zero' ); + $response = API::instance()->maybe_append_source_link( 'Answer.', $post_ids ); + $this->assertSame( 'Answer.', $response ); + remove_all_filters( 'hyve_source_link_limit' ); + } + + /** + * Non-public sources are skipped; the limit counts only rendered links. + */ + public function test_skips_non_public_sources() { + $public = self::factory()->post->create( [ 'post_status' => 'publish' ] ); + $private = self::factory()->post->create( [ 'post_status' => 'private' ] ); + $public2 = self::factory()->post->create( [ 'post_status' => 'publish' ] ); + + $response = API::instance()->maybe_append_source_link( 'Answer.', [ $public, $private, $public2 ] ); + + $this->assertSame( 2, $this->count_links( $response ) ); + } + + /** + * A single scalar ID is still accepted for convenience. + */ + public function test_accepts_single_id() { + $post_id = self::factory()->post->create( [ 'post_status' => 'publish' ] ); + + $response = API::instance()->maybe_append_source_link( 'Answer.', $post_id ); + + $this->assertSame( 1, $this->count_links( $response ) ); + } + + /** + * Invoke the private rank_sources() method. + * + * @param array $scores Source score map. + * + * @return array + */ + private function rank( $scores ) { + $api = API::instance(); + $method = new ReflectionMethod( $api, 'rank_sources' ); + $method->setAccessible( true ); + + return $method->invoke( $api, $scores ); + } + + /** + * A single dominant match drops the low-scoring stragglers. + */ + public function test_ranking_drops_low_scoring_stragglers() { + $ranked = $this->rank( + [ + 'a' => 0.70, + 'b' => 0.42, + 'c' => 0.41, + ] + ); + + $this->assertSame( [ 'a' ], $ranked ); + } + + /** + * Genuinely close matches are all kept, ordered by score. + */ + public function test_ranking_keeps_close_matches_ordered() { + $ranked = $this->rank( + [ + 'b' => 0.66, + 'a' => 0.70, + 'c' => 0.30, + ] + ); + + $this->assertSame( [ 'a', 'b' ], $ranked ); + } + + /** + * The score-ratio filter controls how aggressively stragglers are dropped; + * a ratio of 0 keeps everything that cleared the context threshold. + */ + public function test_score_ratio_filter_can_keep_all() { + add_filter( 'hyve_source_link_score_ratio', '__return_zero' ); + + $ranked = $this->rank( + [ + 'a' => 0.70, + 'b' => 0.42, + 'c' => 0.41, + ] + ); + + $this->assertSame( [ 'a', 'b', 'c' ], $ranked ); + + remove_all_filters( 'hyve_source_link_score_ratio' ); + } +} diff --git a/tests/php/unit/tests/test-stats-endpoint.php b/tests/php/unit/tests/test-stats-endpoint.php new file mode 100644 index 00000000..7aadbe29 --- /dev/null +++ b/tests/php/unit/tests/test-stats-endpoint.php @@ -0,0 +1,59 @@ +user->create( [ 'role' => 'administrator' ] ) ); + + $response = $this->request_stats(); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + + $this->assertArrayHasKey( 'threads', $data['stats'] ); + $this->assertArrayHasKey( 'messages', $data['stats'] ); + $this->assertArrayHasKey( 'totalChunks', $data['stats'] ); + + $this->assertArrayHasKey( 'legend', $data['chart'] ); + $this->assertArrayHasKey( 'labels', $data['chart'] ); + $this->assertArrayHasKey( 'messages', $data['chart']['data'] ); + $this->assertArrayHasKey( 'sessions', $data['chart']['data'] ); + $this->assertSameSize( $data['chart']['labels'], $data['chart']['data']['messages'] ); + } + + /** + * The route is admin-only. + */ + public function testRejectsUnauthorizedUsers() { + wp_set_current_user( 0 ); + + $this->assertEquals( 401, $this->request_stats()->get_status() ); + + wp_set_current_user( self::factory()->user->create( [ 'role' => 'subscriber' ] ) ); + + $this->assertEquals( 403, $this->request_stats()->get_status() ); + } +} diff --git a/tests/php/unit/tests/test-stream.php b/tests/php/unit/tests/test-stream.php new file mode 100644 index 00000000..7469b79b --- /dev/null +++ b/tests/php/unit/tests/test-stream.php @@ -0,0 +1,91 @@ +Hi

","success":true}'; + $this->assertSame( '

Hi

', Stream::extract_partial_response( $json ) ); + } + + /** + * A response field that has begun but not closed yields what is available. + */ + public function testPartialResponse() { + $this->assertSame( '

Hel', Stream::extract_partial_response( '{"response":"

Hel' ) ); + } + + /** + * The response key may appear after other fields. + */ + public function testResponseAfterOtherKeys() { + $json = '{"success":true,"response":"

Hi

"}'; + $this->assertSame( '

Hi

', Stream::extract_partial_response( $json ) ); + } + + /** + * JSON escapes inside the response are decoded. + */ + public function testDecodesEscapes() { + $this->assertSame( "a\nb", Stream::extract_partial_response( '{"response":"a\nb"}' ) ); + $this->assertSame( 'say "hi"', Stream::extract_partial_response( '{"response":"say \"hi\""}' ) ); + $this->assertSame( 'café', Stream::extract_partial_response( '{"response":"café"}' ) ); + } + + /** + * Surrogate-pair characters (emoji) decode to the real character. + */ + public function testDecodesSurrogatePairEmoji() { + $this->assertSame( '😀', Stream::extract_partial_response( '{"response":"😀"}' ) ); + $this->assertSame( 'hi 😀', Stream::extract_partial_response( '{"response":"hi 😀","success":true}' ) ); + } + + /** + * A lone high surrogate (awaiting its low half) is held back, not emitted broken. + */ + public function testHoldsLoneHighSurrogate() { + $this->assertSame( 'hi ', Stream::extract_partial_response( '{"response":"hi \uD83D' ) ); + } + + /** + * An incomplete \u escape is held back. + */ + public function testHoldsIncompleteUnicodeEscape() { + $this->assertSame( 'a', Stream::extract_partial_response( '{"response":"a\u00' ) ); + } + + /** + * A literal multibyte character split across the buffer is held back, not + * emitted as a broken byte sequence. + */ + public function testHoldsSplitMultibyte() { + // 'café' as raw UTF-8, cut after the first byte of 'é' (0xC3). + $this->assertSame( 'caf', Stream::extract_partial_response( "{\"response\":\"caf\xc3" ) ); + } + + /** + * A trailing, incomplete escape is held back rather than emitted broken. + */ + public function testIncompleteTrailingEscape() { + $this->assertSame( 'a', Stream::extract_partial_response( '{"response":"a\\' ) ); + } + + /** + * Null until the response field has started. + */ + public function testNullBeforeResponseField() { + $this->assertNull( Stream::extract_partial_response( '{"succ' ) ); + $this->assertNull( Stream::extract_partial_response( '' ) ); + } +} diff --git a/tests/php/unit/tests/test-threads.php b/tests/php/unit/tests/test-threads.php new file mode 100644 index 00000000..2ecbc404 --- /dev/null +++ b/tests/php/unit/tests/test-threads.php @@ -0,0 +1,77 @@ +assertSame( 0, (int) Threads::get_messages_count() ); + $this->assertNotFalse( get_transient( Threads::MESSAGES_COUNT_TRANSIENT ) ); + + Threads::create_thread( + 'Hello', + [ + 'thread_id' => 'thread-1', + 'sender' => 'user', + 'message' => 'Hello', + ] + ); + + // The stale cache must be gone so the next read recomputes. + $this->assertFalse( get_transient( Threads::MESSAGES_COUNT_TRANSIENT ) ); + $this->assertSame( 1, (int) Threads::get_messages_count() ); + } + + /** + * Adding a message to a thread keeps the messages count current. + */ + public function test_add_message_updates_count() { + $post_id = Threads::create_thread( + 'Hello', + [ + 'thread_id' => 'thread-1', + 'sender' => 'user', + 'message' => 'Hello', + ] + ); + + // Prime the cache at 1 (the user message). + $this->assertSame( 1, (int) Threads::get_messages_count() ); + + Threads::add_message( + $post_id, + [ + 'thread_id' => 'thread-1', + 'sender' => 'bot', + 'message' => 'Hi there', + ] + ); + + // The bot reply must be reflected immediately, not after the cache TTL. + $this->assertSame( 2, (int) Threads::get_messages_count() ); + } +} diff --git a/tests/php/unit/tests/test-update-meta.php b/tests/php/unit/tests/test-update-meta.php new file mode 100644 index 00000000..962547b6 --- /dev/null +++ b/tests/php/unit/tests/test-update-meta.php @@ -0,0 +1,92 @@ +post->create(); + update_post_meta( $post_id, '_hyve_added', 1 ); + + wp_update_post( + [ + 'ID' => $post_id, + 'post_title' => 'Edited title', + ] + ); + + $this->assertEquals( 1, get_post_meta( $post_id, '_hyve_needs_update', true ) ); + } + + /** + * Content that was never indexed is not flagged. + */ + public function testUnindexedPostIsNotFlagged() { + $post_id = self::factory()->post->create(); + + wp_update_post( + [ + 'ID' => $post_id, + 'post_title' => 'Edited title', + ] + ); + + $this->assertEmpty( get_post_meta( $post_id, '_hyve_needs_update', true ) ); + } + + /** + * Pipeline-owned entries (the non-viewable `hyve_docs` type) are exempt. + */ + public function testPipelineEntriesAreNeverFlagged() { + $post_id = wp_insert_post( + [ + 'post_type' => 'hyve_docs', + 'post_status' => 'publish', + 'post_title' => 'Imported page', + ] + ); + update_post_meta( $post_id, '_hyve_added', 1 ); + + wp_update_post( + [ + 'ID' => $post_id, + 'post_title' => 'Refreshed import', + ] + ); + + $this->assertEmpty( get_post_meta( $post_id, '_hyve_needs_update', true ) ); + } + + /** + * Editing an indexed post clears a previous moderation failure so it can + * be re-checked. + */ + public function testEditClearsModerationFlags() { + $post_id = self::factory()->post->create(); + update_post_meta( $post_id, '_hyve_added', 1 ); + update_post_meta( $post_id, '_hyve_moderation_failed', 1 ); + update_post_meta( $post_id, '_hyve_moderation_review', [ 'hate' => 0.5 ] ); + + wp_update_post( + [ + 'ID' => $post_id, + 'post_title' => 'Edited title', + ] + ); + + $this->assertEmpty( get_post_meta( $post_id, '_hyve_moderation_failed', true ) ); + $this->assertEmpty( get_post_meta( $post_id, '_hyve_moderation_review', true ) ); + } +} diff --git a/tests/php/unit/tests/test-visibility.php b/tests/php/unit/tests/test-visibility.php new file mode 100644 index 00000000..8a51a55f --- /dev/null +++ b/tests/php/unit/tests/test-visibility.php @@ -0,0 +1,190 @@ +main = new Main(); + } + + /** + * Clean up. + */ + protected function tearDown(): void { + delete_option( 'hyve_settings' ); + unset( $_SERVER['REQUEST_URI'] ); + parent::tearDown(); + } + + /** + * Store raw settings. + * + * @param array $settings Settings. + */ + private function set_settings( $settings ) { + update_option( 'hyve_settings', $settings ); + } + + /** + * A disabled legacy flag derives the manual mode. + */ + public function test_derive_manual_from_disabled_chat() { + $this->set_settings( [ 'chat_enabled' => false ] ); + + $this->assertSame( 'manual', Main::get_settings()['display_mode'] ); + } + + /** + * An enabled (or unset) legacy flag derives the all-pages mode. + */ + public function test_derive_all_from_enabled_chat() { + $this->set_settings( [ 'chat_enabled' => true ] ); + $this->assertSame( 'all', Main::get_settings()['display_mode'] ); + + $this->set_settings( [] ); + $this->assertSame( 'all', Main::get_settings()['display_mode'] ); + } + + /** + * A persisted display_mode always wins over the legacy flag. + */ + public function test_explicit_display_mode_is_preserved() { + $this->set_settings( + [ + 'chat_enabled' => false, + 'display_mode' => 'include', + ] + ); + + $this->assertSame( 'include', Main::get_settings()['display_mode'] ); + } + + /** + * All-pages mode always displays. + */ + public function test_all_mode_displays() { + $this->set_settings( [ 'display_mode' => 'all' ] ); + $this->go_to( home_url( '/' ) ); + + $this->assertTrue( $this->main->should_display_chat() ); + } + + /** + * Manual mode never auto-displays. + */ + public function test_manual_mode_does_not_display() { + $this->set_settings( [ 'display_mode' => 'manual' ] ); + + $this->assertFalse( $this->main->should_display_chat() ); + } + + /** + * A "contains" rule shows the chat on any URL under that path in include mode. + */ + public function test_include_path_contains() { + $this->set_settings( + [ + 'display_mode' => 'include', + 'display_rules' => [ + [ + 'path' => '/shop', + 'operator' => 'contains', + ], + ], + ] + ); + + $_SERVER['REQUEST_URI'] = '/shop/product-a/'; + $this->assertTrue( $this->main->should_display_chat() ); + + $_SERVER['REQUEST_URI'] = '/about/'; + $this->assertFalse( $this->main->should_display_chat() ); + } + + /** + * A "matches" rule requires an exact URL in include mode. + */ + public function test_include_path_matches_exact() { + $this->set_settings( + [ + 'display_mode' => 'include', + 'display_rules' => [ + [ + 'path' => '/contact/', + 'operator' => 'matches', + ], + ], + ] + ); + + $_SERVER['REQUEST_URI'] = '/contact/'; + $this->assertTrue( $this->main->should_display_chat() ); + + $_SERVER['REQUEST_URI'] = '/contact/sub/'; + $this->assertFalse( $this->main->should_display_chat() ); + } + + /** + * Exclude mode is the inverse of include: matching URLs are hidden. + */ + public function test_exclude_inverts_matching() { + $this->set_settings( + [ + 'display_mode' => 'exclude', + 'display_rules' => [ + [ + 'path' => '/shop', + 'operator' => 'contains', + ], + ], + ] + ); + + $_SERVER['REQUEST_URI'] = '/shop/product-a/'; + $this->assertFalse( $this->main->should_display_chat() ); + + $_SERVER['REQUEST_URI'] = '/about/'; + $this->assertTrue( $this->main->should_display_chat() ); + } + + /** + * The SDK migration persists display_mode from the legacy flag, then stops. + */ + public function test_migration_persists_display_mode() { + update_option( 'hyve_settings', [ 'chat_enabled' => false ] ); + + if ( ! class_exists( '\ThemeisleSDK\Modules\Abstract_Migration' ) ) { + require_once HYVE_LITE_PATH . '/vendor/codeinwp/themeisle-sdk/src/Modules/Abstract_Migration.php'; + } + + $migration = require HYVE_LITE_PATH . '/migrations/20260703120000_migrate_chat_display_mode.php'; + + $this->assertTrue( $migration->should_run() ); + + $migration->up(); + + $saved = get_option( 'hyve_settings' ); + $this->assertSame( 'manual', $saved['display_mode'] ); + $this->assertFalse( $migration->should_run() ); + } +}